常量池详解
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以上->trueString中的intern方法是一个 native 的方法,当调用 intern方法时,如果常量池已经包含一个等于此String对象的字符串(用equals(object)方法确定),则返回池中的字符串。
否则,将intern返回的引用指向当前字符串 s1(JDK6版本需要将 s1 复制到字符串常量池里)
八种基本类型的包装类和对象池
-
java中基本类型的包装类的大部分都实现了常量池技术(严格来说应该叫对象池,并且在堆上),这些类包含
- Byte
- Short
- Integer
- Long
- Character
- Boolean
-
以上这六种,除了Boolean类型,其他五种对应-128到127才可使用对象池,即对象不负责创建和管理 > 127 的对象。因为这种小的数用到的概率比较大 -
另外两种浮点数类型的包装类没有实现对象池技术: Double、 Float