方法区简介
方法区用来存储被虚拟机类加载器加载的类的信息、常量、静态变量、编译后的字节码等数据.
谈到方法区就不得不谈一下类的加载过程。虚拟机将类的.class文件加载到内存把它放到方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据,然后在方法区中创建引用指向这个Class类的实例。
常量池
在方法区中最值得一提的便是常量池,它分为静态常量池和运行时常量池。
静态常量池
静态常量池即.class文件中的常量池,class文件中的常量池不仅仅包含字符串或者数字等字面常量,还包含类和方法等信息,主要分为两大类:字面量 和 符号引用量:
- 字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等。
- 符号引用量分为三种:类和接口的全限定名、字段名称和描述符、方法名称和描述符。
这里字面量比较好理解,主要说说符号引用量。在编译时,编译器并不知道java类中所引用的类、字段以及方法的实际地址,只能通过符号引用来代替,在class文件中他们分别以CONSTANT_Class_info、CONSTANT_Fieldref_info 、CONSTANT_Methodref_info结构体类型的常量来出现。java虚拟机规范中对其进行了说明,例如CONSTANT_Methodref_info:
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
当所引用的类被加载后,符号引用量则会变成直接引用量,直接引用量包括:
- 直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)
- 相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)
- 一个能间接定位到目标的句柄
运行时常量池
运行时常量池是JVM在完成类装载操作后,将class文件中的常量池载入到内存中。
运行时常量池与静态常量池不同的是,它具有动态性。静态常量池是编译时就产生的,运行时常量池允许运行期间将新的常量放入常量池,例如String.intern()方法。