[TOC]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
參數調整大小。<u>字符串常量池中存儲的是字符串對象的引用,而字符串自己是在堆上分配的(java中的對象基本都在堆上分配)。</u>運行時常量池初始化的時候,字面量的符號引用的初始化會用到字符串常量池。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。<u>全部字面值字符串和字符串賦值常量表達式都使用intern方法進行處理。</u>
示例:
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/