深入JVM——常量池
2017-08-22 16:32
183 查看
上面主要分析了方法区以及方法区中的静态区域,下面将主要分析下常量池。常量池主要涉及到常量池里的内容和常量池解析这两块,这篇文章主要分析下常量池概念,大致说下常量池解析,以加深对常量池的理解。
在方法区中,每个类型都对应一个常量池,常量池中存储了诸如文字字符串、final变量值、类名和方法名常量。JVM把常量池组织为入口列表的形式,可通过索引来访问常量池中的各个入口,每个常量池入口的第一个字节都是个标志,用这个标志来表示该入口中存储的常量类型,如CONSTANT_Long表示里面存储的是long类型字面值,CONSTANT_Class_info表示里面存的是某个Class的类型信息(存的可能是个普通的字符串,然后经过常量池解析,则变成指向某个类的引用)。
除了字面常量值以外,常量池还可以容纳其它几种符号引用:类和接口的全限定名、字段名称和描述符、方法名称和描述符。
类和接口的全限定名指的是当前类的全限定名。
字段名称指的是类或接口的实例变量或类变量,字段的描述符是一个指示字段的类型的字符串。如在一个类中有以下形式的声明:A a = null, 则a为字段名,A为字段描述符。
方法的描述符也是个字符串,该字符串指示了方法的返回值和参数的数量、顺序和类型。
在运行时,JVM从常量池中获得符号引用,然后在运行时解析成引用项的实际地址,最后通过常量池中的全限定名、方法和字段描述符,把当前类或接口中的代码与其它类或接口中的代码联系起来。
讲到常量池,就涉及到常量池解析,常量池解析是非常复杂的,它分多种情况处理。这里,就列举个简单的例子,大家看看,大概了解下常量池解析的大概过程。
测试例子程序:
Java代码
/**
* Dog的超类是个接口,而不是类
*/
public class Animal {
String id = "Animal"; //当类变量是static final
public void print() {
System.out.println("this is animal"+id);
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
}
}
下面将描述下JVM是如何处理Test类中的main()方法的第一条指令:
要运行Test程序,JVM找到对应的Test.class文件,并读入到方法区中。通过保存在方法区中的字节码,JVM开始执行main方法,执行时,它会一直指向当前类(Test类)的常量池。 main的第一条指令告诉JVM要为常量池中的第一项类分配足够的内存,于是JVM使用Test常量池指针找到第一项,发现它是一个对Animal类的符号引用(也就是对字符串“Animal”的引用),于是JVM检查方法区,看Animal类是否被装载了。
如JVM发现还没装载过Animal类时,它开始查找并装载Animal.class文件。
最后,JVM将一个直接指向方法区Animal类数据的指针来代替常量池第一项(也就是那个字符串”Animal”),以后,则可以通过该指针来快速访问Animal类了,这个就称为常量池解析。
说了那么多,常量池解析在这里作的处理就是把常量池中的字符串"Animal"解析成一个对类Animal的引用。
在方法区中,每个类型都对应一个常量池,常量池中存储了诸如文字字符串、final变量值、类名和方法名常量。JVM把常量池组织为入口列表的形式,可通过索引来访问常量池中的各个入口,每个常量池入口的第一个字节都是个标志,用这个标志来表示该入口中存储的常量类型,如CONSTANT_Long表示里面存储的是long类型字面值,CONSTANT_Class_info表示里面存的是某个Class的类型信息(存的可能是个普通的字符串,然后经过常量池解析,则变成指向某个类的引用)。
除了字面常量值以外,常量池还可以容纳其它几种符号引用:类和接口的全限定名、字段名称和描述符、方法名称和描述符。
类和接口的全限定名指的是当前类的全限定名。
字段名称指的是类或接口的实例变量或类变量,字段的描述符是一个指示字段的类型的字符串。如在一个类中有以下形式的声明:A a = null, 则a为字段名,A为字段描述符。
方法的描述符也是个字符串,该字符串指示了方法的返回值和参数的数量、顺序和类型。
在运行时,JVM从常量池中获得符号引用,然后在运行时解析成引用项的实际地址,最后通过常量池中的全限定名、方法和字段描述符,把当前类或接口中的代码与其它类或接口中的代码联系起来。
讲到常量池,就涉及到常量池解析,常量池解析是非常复杂的,它分多种情况处理。这里,就列举个简单的例子,大家看看,大概了解下常量池解析的大概过程。
测试例子程序:
Java代码
/**
* Dog的超类是个接口,而不是类
*/
public class Animal {
String id = "Animal"; //当类变量是static final
public void print() {
System.out.println("this is animal"+id);
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
}
}
下面将描述下JVM是如何处理Test类中的main()方法的第一条指令:
要运行Test程序,JVM找到对应的Test.class文件,并读入到方法区中。通过保存在方法区中的字节码,JVM开始执行main方法,执行时,它会一直指向当前类(Test类)的常量池。 main的第一条指令告诉JVM要为常量池中的第一项类分配足够的内存,于是JVM使用Test常量池指针找到第一项,发现它是一个对Animal类的符号引用(也就是对字符串“Animal”的引用),于是JVM检查方法区,看Animal类是否被装载了。
如JVM发现还没装载过Animal类时,它开始查找并装载Animal.class文件。
最后,JVM将一个直接指向方法区Animal类数据的指针来代替常量池第一项(也就是那个字符串”Animal”),以后,则可以通过该指针来快速访问Animal类了,这个就称为常量池解析。
说了那么多,常量池解析在这里作的处理就是把常量池中的字符串"Animal"解析成一个对类Animal的引用。
相关文章推荐
- 深入理解JVM : class文件结构之常量池(1)
- 深入JVM——常量池
- 深入JVM 原理(九)JVM垃圾回收策略参数配置
- 深入Java虚拟机:JVM中的Stack和Heap
- JVM内存管理:深入垃圾收集器与内存分配策略
- 深入理解JVM内部结构(续)
- 深入理解JVM虚拟机 第七章笔记 虚拟机类加载机制
- 深入JVM——类型的生命周期(二)
- 理解Java常量池--从JVM原理上理解字符串的比较
- 深入分析JVM逃逸分析对性能的影响
- JVM 深入笔记(3)垃圾标记算法
- JVM内存管理:深入Java内存区域与OOM
- 深入理解JVM之类文件结构
- JVM内存管理:深入垃圾收集器与内存分配策略
- JVM 深入笔记(2)内存溢出场景模拟
- 深入理解JVM垃圾回收
- 深入了解JVM
- 深入理解JVM(一)——运行时的数据区域
- 深入理解JVM(四)——各个版本提供的垃圾收集器
- 深入理解Java虚拟机 -- 读书笔记(1):JVM运行时数据区域