在Java的內存分配中,總共3種常量池:javascript
轉發連接:https://blog.csdn.net/zm13007310400/article/details/77534349java
1.字符串常量池(String Constant Pool):
1.1:字符串常量池在Java內存區域的哪一個位置?
- 在JDK6.0及以前版本,字符串常量池是放在Perm Gen區(也就是方法區)中;
- 在JDK7.0版本,字符串常量池被移到了堆中了。至於爲何移到堆內,大概是因爲方法區的內存空間過小了。
- JDK8之後也仍是放在了Heap空間中,並無已到元空間。
1.2:字符串常量池是什麼?
- 在HotSpot VM裏實現的string pool功能的是一個StringTable類,它是一個Hash表,默認值大小長度是1009;這個StringTable在每一個HotSpot VM的實例只有一份,被全部的類共享。字符串常量由一個一個字符組成,放在了StringTable上。
- 在JDK6.0中,StringTable的長度是固定的,長度就是1009,所以若是放入String Pool中的String很是多,就會形成hash衝突,致使鏈表過長,當調用String#intern()時會須要到鏈表上一個一個找,從而致使性能大幅度降低;
- 在JDK7.0中,StringTable的長度能夠經過參數指定:
-XX:StringTableSize=66666
1.3:字符串常量池裏放的是什麼?
- 在JDK6.0及以前版本中,String Pool裏放的都是字符串常量;
- 在JDK7.0中,因爲String#intern()發生了改變,所以String Pool中也能夠存放放於堆內的字符串對象的引用。關於String在內存中的存儲和String#intern()方法的說明,能夠參考個人另一篇博客:
須要說明的是:字符串常量池中的字符串只存在一份!
如:ruby
String s1 = "hello,world!"; String s2 = "hello,world!";
即執行完第一行代碼後,常量池中已存在 「hello,world!」,那麼 s2不會在常量池中申請新的空間,而是直接把已存在的字符串內存地址返回給s2。(這裏具體的字符串如何分配就不細說了,能夠看個人另外一篇博客)jvm
2.class常量池(Class Constant Pool):
2.1:class常量池簡介:
- 咱們寫的每個Java類被編譯後,就會造成一份class文件;class文件中除了包含類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池(constant pool table),用於存放編譯器生成的各類字面量(Literal)和符號引用(Symbolic References);
- 每一個class文件都有一個class常量池。
2.2:什麼是字面量和符號引用:
- 字面量包括:1.文本字符串 2.八種基本類型的值 3.被聲明爲final的常量等;
- 符號引用包括:1.類和方法的全限定名 2.字段的名稱和描述符 3.方法的名稱和描述符。
3.運行時常量池(Runtime Constant Pool):
- 運行時常量池存在於內存中,也就是class常量池被加載到內存以後的版本,不一樣之處是:它的字面量能夠動態的添加(String#intern()),符號引用能夠被解析爲直接引用
- JVM在執行某個類的時候,必須通過加載、鏈接、初始化,而鏈接又包括驗證、準備、解析三個階段。而當類加載到內存中後,jvm就會將class常量池中的內容存放到運行時常量池中,由此可知,運行時常量池也是每一個類都有一個。在解析階段,會把符號引用替換爲直接引用,解析的過程會去查詢字符串常量池,也就是咱們上面所說的StringTable,以保證運行時常量池所引用的字符串與字符串常量池中是一致的。
public static void main(String[] args) {
String s1="abc";
String s2="abc";
System.out.println(s1==s2);//true
String s3=new String("ab")+new String("c");
// 這裏顯示false,可能先在字符串常量池中添加s1,而後動態的添加s3,會顯示false
s3.intern();
System.out.println(s1==s3); //false
String s4=new String("ab")+new String("cd");
//s4.intern();
String s5="abcd";
System.out.println(s4==s5);//true,去掉intern以後是false;
}