目录html
class文件常量池位于class文件中java
class文件头4个字节称为魔数,魔数后面的4个字节为文件版本号,而版本号以后的就是常量池的入口。该常量池用于存放编译器生成的各类字面量和符号引用,字面量就是所谓的常量,如字符串,final修饰的常量值等。而符号引用则是用来描述引用目标的,如类和接口的全限定名,方法和字段的名称和描述符。此时符号引用并不会存储最终内存布局信息。git
class文件中方法和字段均须要引用CONSTANT_Utf8
型常量描述名称,该类型的最大长度也就是java中方法和字段名的长度,而该类型的长度用2个字节表示,最大值为65535,因此java中若是定义了超过64kb(2^16*1byte,utf8中一个字符占一个字节)的英文字符的变量或方法名,将没法编译。github
当类或接口建立时,它的class中的常量池会被用来构造运行时常量池,常量池中的符号引用会被解析成具体的内存地址。运行时常量池是jvm方法区的一部分,它能够在运行时将符号引用解析为直接引用。bash
运行时常量池位于jvm的元空间中(java8)app
字符串常量池底层实现是一个哈希表,能够经过-XX:StringTableSize
参数调整大小。字符串常量池中存储的是字符串对象的引用,而字符串自己是在堆上分配的(java中的对象基本都在堆上分配)。运行时常量池初始化的时候,字面量的符号引用的初始化会用到字符串常量池。String中的intern方法能够在运行时将字符串实例加入字符串常量池。jvm
在java1.7之前,字符串常量池是在堆的永久代里面,大小固定,而从java1.7之后,字符串常量池则移动到java堆中了。布局
验证代码以下:spa
public class test { private static String str = ""; public static void main(String[] args) { //-Xms32m -Xmx32m char element = 'a'; StringBuffer sb = new StringBuffer(); for (int i = 0; i < 1024*1024*64; i++) { sb.append(element); } str = sb.toString(); System.out.println(); } }
JVM启动参数:-Xms32m -Xmx32m
.net
运行结果以下:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space ...... at test.test.main(test.java:17)
能够发现堆发生溢出(oom),说明常量池在堆中分配。
String类的intern()方法的描述是:String类维护着一个初始化为空的字符串池,当某String实例调用intern()方法的时候,若是字符串池中已包含与此字符串相同的字符串(用equal(obj)方法判断相等),则返回池中的字符串对象。不然,将此字符串对象加入到常量池中,并返回此字符串对象的引用。对于任意两个字符串s和t,当切仅当s.equals(t)为true,s.intern() ==t.intern()才为true。全部字面值字符串和字符串赋值常量表达式都使用intern方法进行处理。
示例:
public class InternTest { public static void main(String[] args) { String s1 = new String("a"); s1.intern(); String s2 = "a"; System.out.println(s1 == s2); String s3 = new String("b") + new String("b"); s3.intern(); String s4 = "bb"; System.out.println(s3 == s4); } }
运行结果以下:
false true
能够经过jvm参数-XX:StringTableSize
来配置String常量池的大小,在java6和java7u40以前的版本,其默认大小为1009,java7u40以及以后的java8中其默认值为60013。其值要求得是素数。
new
关键字和使用字符串字面量的区别示例:
String str1="abc";
String str2=new String("abc");
当使用String str="abc"
这种形式的时候,JVM会在字符串常量池中建立该对象而且str1会指向这个对象;
当使用String str=new String("abc")
这种形式的时候,JVM会先检查在字符串常量池中是否已存在"abc"
,若是不存在则在常量池中建立该对象,若是已存在则不建立。除此以外,还会在堆外建立一个额外的字符串对象,而且str2会指向这个对象。
验证代码:
public class test { public static void main(String[] args) { String str1 = "abc"; String str2 = new String("abc"); System.out.println(str1); System.out.println(str2); System.out.println(str1 == str2); } }
运行结果以下:
abc abc false
查看该代码的字节码,会发现两种建立字符串对象的方式的不一样:
L0 LINENUMBER 9 L0 LDC "abc" //从常量池加载String ASTORE 1
L1 LINENUMBER 10 L1 NEW java/lang/String //建立一个String对象 DUP LDC "abc" //从常量池加载String INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V //调用构造方法,实例初始化 ASTORE 2
经过对比发现,使用new关键字与直接使用字符串字面量相比,多了建立对象的过程。
先看一个示例:
public class test { public static void main(String[] args) { System.out.println("---------String with new--------"); collectWeakReference(new String("dasfsafsafsafsa")); System.out.println("---------String with literal--------"); collectWeakReference("dsafdsafxcdfeghg"); } private static void collectWeakReference(String obj ) { ReferenceQueue<String> rq = new ReferenceQueue<>(); Reference<String> r = new WeakReference<>(obj, rq); obj = null; Reference rf; int gccount = 10; //一次System.gc()并不必定会回收A,因此要多试几回 while((rf=rq.poll()) == null && gccount >=0) { System.gc(); gccount--; } System.out.println(rf); if (rf != null) {//若是对象被回收则弱引用会加入引用队列 //引用指向的对象已经被回收,存入引入队列的是弱引用自己,因此这里最终返回null System.out.println(rf.get()); } } }
运行结果以下:
---------String with new-------- java.lang.ref.WeakReference@5a07e868 null ---------String with literal-------- null
因而可知,指向new关键字建立的字符串对象的弱引用会被System.gc()触发的gc回收掉,而指向字面量字符串的弱引用则不会被System.gc()触发的gc回收掉。这说明new关键字建立的字符串对象若是不可达了会被gc回收,而字符串字面量建立的字符串对象则不会,由于常量池中还持有对该字面量字符串的引用。
参考连接:
https://netsurfingzone.com/core-java/string-constant-pool-in-java
https://tangxman.github.io/2015/07/27/the-difference-of-java-string-pool/
http://www.javashuo.com/article/p-klishdbi-mn.html
https://stackoverflow.com/questions/23252767/string-pool-vs-constant-pool
http://java-performance.info/string-intern-in-java-6-7-8/
https://netsurfingzone.com/core-java/string-constant-pool-in-java
http://www.javashuo.com/article/p-dttasbwf-ck.html
https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html
https://blog.csdn.net/qq_26929957/article/details/79090406
https://jimmy2angel.github.io/2018/10/26/ConstantPool/
https://droidyue.com/blog/2014/12/21/string-literal-pool-in-java/