常量池详解
Class常量池与运行时常量池
- Class常量池可以理解为字节码文件中的资源仓库,字节码文件包括除了魔数、主次版本号、接口、方法等描述信息外,还有一项信息就是
常量池
,用于存放编译期间生成的各种字面量和符号引用
问题
: 什么是运行时常量池、Class常量池、全局常量池Class常量池
: 存放编译期间生成的各种字面量和符号引用运行时常量池
: JVM启动的时候,将常量池的静态数据加载到方法区中去全局常量池
: 存放的是字符串的引用值,JVM只有一份
问题
: 什么是字面量- 字面量就是变量的值,比如String a = “abc”,"abc"就是字面量
问题
: 什么是符号引用- 方法名和描述符
- 字段的名称和描述符
- 类和接口名称
问题
: 什么是动态链接程序运行期间,将符号引用转变为直接引用
,比如方法中调用方法,JVM解析的时候会对被调用的方法名指向一个内存地址,通过这个地址找到对应的方法,最常见的就是多态
,有不同的实现,只有到运行的时候才知道- 举个例子: 比如math.compute()
- 静态的时候compute()就是符号引用,加载到内存后就有内存地址,这个地址就是符号引用在内存中的地址,叫直接引用,根据这个内存地址可以找到这个方法在方法区里的代码信息,类似于函数指针
字符串常量池
注意
: 在JDK6中,字符串常量池在运行时常量池中,在JDK7放在了堆中- 字符串常量池类似于缓存池,在创建任何一个字符串之前,都会去这个常量池查一下,有就直接返回,没有就新建
- 例如: String s = new String(“a”),会生成两个引用,一个字符串常量池中,一个在堆中
JDK7+常量池
-
直接赋值字符串:
常量池中没有就创建对象,有匹配后直接返回
String s = "abc";
这种创建的字符串对象,只会在常量池中,创建对象的时候,JVM会先去常量池中通过equals方法,判断是否有相同的对象,如果有,返回它在常量池中的引用,如果没有,就在常量池中创建一个对象,返回它的引用
-
new String():
如果常量池和堆中都有这个对象,就直接把堆中引用返回,如果常量池有这个字符串对象,就只要在堆中创建一个字符串对象,如果不存在,得在字符串和堆中都创建该对象,最后把堆中内存返回
String s = new String("abc");
这种方式保证常量池和堆中都有这个对象,没有就创建,最后返回堆内存中的对象引用(生成两个引用)
先检查字符串常量池中是否存在这个字符串,
不存在,就在常量池中创建一个字符串对象,然后再去堆内存中创建一个字符串对象
存在,就直接在堆内存中创建一个对象.
最后将堆内存中的引用返回.
-
intern()
String s1 = new String("abc"); String s2 = s1.intern(); System.out.println(s1 == s2); // JDK6->false JDK7以上->true
String中的intern方法是一个 native 的方法,当调用 intern方法时,如果常量池已经包含一个等于此String对象的字符串(用equals(object)方法确定),则返回池中的字符串。
否则,将intern返回的引用指向当前字符串 s1(JDK6版本需要将 s1 复制到字符串常量池里)
八种基本类型的包装类和对象池
- java中基本类型的包装类的大部分都实现了常量池技术(严格来说应该叫对象池,并且在堆上),这些类包含
- Byte
- Short
- Integer
- Long
- Character
- Boolean
以上这六种,除了Boolean类型,其他五种对应-128到127才可使用对象池,即对象不负责创建和管理 > 127 的对象。因为这种小的数用到的概率比较大
- 另外两种浮点数类型的包装类没有实现对象池技术: Double、 Float