目录
栈、堆、方法区的交互关系
方法区的理解
设置方法区内存的大小
方法区的内部结构
概述
类型信息
域信息(Field)
方法信息
静态变量
常量池表和运行时常量池
比较
常量池表
运行时常量池
总结:方法区存储了什么
方法区历代版本的演进
历代版本图
为什么要把永久代换成元空间?
StringTable为什么要调整
方法区的垃圾回收
栈、堆、方法区的交互关系
- 运行时数据区结构图
- 我们学习的HotSpot用的是左图这种直接指针的方式,但是还有一种句柄池的方式(《深入理解Java虚拟机》),如右图所示
方法区的理解
- JDK1.7之前方法区又叫永久代PermGen space,1.7之后叫做元空间Metaspace, 元空间使用的是本地内存,不使用jvm的内存。
设置方法区内存的大小
- jdk7之后这个指令是无效的,因为已经替换成元空间了
- jdk8及以后都是设置元空间命令 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 指定
方法区的内部结构
概述
- 字节码文件如下
D:\buildproject\jvm-study\out\production\jvm-01\com\vuhen\jvmMethod>javap -v -p MethodDemo.class
Classfile /D:/buildproject/jvm-study/out/production/jvm-01/com/vuhen/jvmMethod/MethodDemo.class
Last modified 2021-5-12; size 1095 bytes
MD5 checksum 0f0987d697ef358a1a47c75699f74abf
Compiled from "MethodDemo.java"
//类型信息
public class com.vuhen.jvmMethod.MethodDemo extends java.lang.Object implements java.util.Comparator<java.lang.String>
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
//常量池
Constant pool:
#1 = Methodref #8.#38 // java/lang/Object."<init>":()V
#2 = Fieldref #7.#39 // com/vuhen/jvmMethod/MethodDemo.publicVal:I
#3 = Fieldref #7.#40 // com/vuhen/jvmMethod/MethodDemo.priVal:I
#4 = Class #41 // java/lang/String
#5 = Methodref #7.#42 // com/vuhen/jvmMethod/MethodDemo.compare:(Ljava/lang/String;Ljava/lang/String;)I
#6 = Fieldref #7.#43 // com/vuhen/jvmMethod/MethodDemo.priStaticVal:I
#7 = Class #44 // com/vuhen/jvmMethod/MethodDemo
#8 = Class #45 // java/lang/Object
#9 = Class #46 // java/util/Comparator
#10 = Utf8 publicVal
#11 = Utf8 I
#12 = Utf8 priStaticVal
#13 = Utf8 priVal
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 LocalVariableTable
#19 = Utf8 this
#20 = Utf8 Lcom/vuhen/jvmMethod/MethodDemo;
#21 = Utf8 compare
#22 = Utf8 (Ljava/lang/String;Ljava/lang/String;)I
#23 = Utf8 o1
#24 = Utf8 Ljava/lang/String;
#25 = Utf8 o2
#26 = Utf8 priMethod
#27 = Utf8 (I)I
#28 = Utf8 a
#29 = Utf8 i
#30 = Utf8 b
#31 = Utf8 publicStaticMethod
#32 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)I
#33 = Utf8 <clinit>
#34 = Utf8 Signature
#35 = Utf8 Ljava/lang/Object;Ljava/util/Comparator<Ljava/lang/String;>;
#36 = Utf8 SourceFile
#37 = Utf8 MethodDemo.java
#38 = NameAndType #14:#15 // "<init>":()V
#39 = NameAndType #10:#11 // publicVal:I
#40 = NameAndType #13:#11 // priVal:I
#41 = Utf8 java/lang/String
#42 = NameAndType #21:#22 // compare:(Ljava/lang/String;Ljava/lang/String;)I
#43 = NameAndType #12:#11 // priStaticVal:I
#44 = Utf8 com/vuhen/jvmMethod/MethodDemo
#45 = Utf8 java/lang/Object
#46 = Utf8 java/util/Comparator
{
//成员变量 域信息
public int publicVal;
descriptor: I //类型int
flags: ACC_PUBLIC //修饰符public
private static int priStaticVal;
descriptor: I //类型int
flags: ACC_PRIVATE, ACC_STATIC //修饰符private,static
private int priVal;
descriptor: I//类型int
flags: ACC_PRIVATE //修饰符private
//构造器
public com.vuhen.jvmMethod.MethodDemo();
descriptor: ()V //()表示参数为空 V表示返回值为void
flags: ACC_PUBLIC //修饰符public
Code:
//栈深度(栈执行中最大的长度);局部变量表的大小;参数个数
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_5
6: putfield #2 // Field publicVal:I
9: aload_0
10: iconst_3
11: putfield #3 // Field priVal:I
14: return
LineNumberTable:
line 5: 0
line 6: 4
line 8: 9
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this Lcom/vuhen/jvmMethod/MethodDemo;
//方法信息
public int compare(java.lang.String, java.lang.String);
descriptor: (Ljava/lang/String;Ljava/lang/String;)I //参数String;String 返回值int
flags: ACC_PUBLIC
Code:
stack=1, locals=3, args_size=3
0: iconst_0
1: ireturn
LineNumberTable:
line 11: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this Lcom/vuhen/jvmMethod/MethodDemo;
0 2 1 o1 Ljava/lang/String;
0 2 2 o2 Ljava/lang/String;
public int priMethod(int);
descriptor: (I)I //参数int 返回值int
flags: ACC_PUBLIC //修饰符public
Code:
stack=2, locals=4, args_size=2
0: iconst_5
1: istore_2
2: iconst_4
3: istore_3
4: iload_2
5: iload_3
6: iadd
7: iload_1
8: iadd
9: ireturn
LineNumberTable:
line 15: 0
line 16: 2
line 17: 4
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/vuhen/jvmMethod/MethodDemo;
0 10 1 a I
2 8 2 i I
4 6 3 b I
public static void publicStaticMethod();
descriptor: ()V //无参数无返回值
flags: ACC_PUBLIC, ACC_STATIC //修饰符public static
Code:
stack=1, locals=1, args_size=0
0: iconst_5
1: istore_0
2: return
LineNumberTable:
line 21: 0
line 22: 2
LocalVariableTable:
Start Length Slot Name Signature
2 1 0 i I
public int compare(java.lang.Object, java.lang.Object);
descriptor: (Ljava/lang/Object;Ljava/lang/Object;)I //参数Object,Object 返回值int
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC //修饰符
Code:
stack=3, locals=3, args_size=3
0: aload_0
1: aload_1
2: checkcast #4 // class java/lang/String
5: aload_2
6: checkcast #4 // class java/lang/String
9: invokevirtual #5 // Method compare:(Ljava/lang/String;Ljava/lang/String;)I
12: ireturn
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 this Lcom/vuhen/jvmMethod/MethodDemo;
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: iconst_4
1: putstatic #6 // Field priStaticVal:I
4: return
LineNumberTable:
line 7: 0
}
Signature: #35 // Ljava/lang/Object;Ljava/util/Comparator<Ljava/lang/String;>;
SourceFile: "MethodDemo.java"
类型信息
域信息(Field)
方法信息
静态变量
- 静态变量是和类绑定在一起的,即使没有实例也能够访问,下面的代码是能运行的,不会报错
- 注:静态变量仍存放在堆中
常量池表和运行时常量池
比较
- 常量池表是字节码的一部分,运行时常量池是方法区的一部分 常量池表中的内容会在类加载完后放到运行时常量池中
- 在运行后就会生成运行时常量池,而字节码中的常量池表可以理解为就是运行时常量池中信息的索引
常量池表
- 一个小小的类里面其实使用了非常多的引用和类,但是下面生成的字节码却非常小,原因就是因为有常量池表,用来存储对类型、域和方法符号引用以及各种字面量
- 下面这个字节码的大小就说明了常量池表的存在
- 如下图所示,方法执行时都是调用常量池表的符号引用,符号引用再到常量池中找信息
- ldc最终找到的常量池中信息就是ceshi1 这个称为字面量
运行时常量池
- 常量池表在类加载后就会把信息全部都加载到运行时常量池;运行时常量池时具有动态性的 运行期间也可能产生新的常量,这些常量被放在运行时常量池中。
总结:方法区存储了什么
- 像什么常量池啊,类信息,域信息,方法信息,方法中的指令信息等等都是存在方法区的
方法区历代版本的演进
历代版本图
- JDK8之前方法区用的还是虚拟机的内存,8及以后用的就是本地内存了
- StringTable指的是字符串常量池
为什么要把永久代换成元空间?
StringTable为什么要调整
方法区的垃圾回收