前言
方法區(Method Area)是執行緒共享的一塊記憶體區域,JVM載入的型別資訊,常量,靜態變數和即時編譯器編譯後的程式碼快取等資料均存放於方法區。
執行時常量池(Runtime Constant Pool)是方法區的一部分,在Class
檔案中有一部分內容為常量池表(Constant Pool Table),用於存放編譯期生成的各種字面量與符號引用,這部分內容在Class
檔案被載入到JVM後會被存放在執行時常量池中。
字串常量池中存放字串字面量,在JDK1.8中,字串常量池存在於堆中。
本篇文章將對JDK1.8中的方法區,執行時常量池和字串常量池的區域分佈進行說明,並著重對字串常量池進行分析以探究new
一個字串物件時到底會在堆上建立幾個物件。
JDK版本:1.8
參考資料:《深入理解Java
虛擬機器第三版》
正文
一. 方法區的區域分佈
方法區只是一個邏輯概念,在JDK1.8中方法區的具體實現為元空間,而元空間使用的是本地記憶體。在JDK1.8中,方法區,執行時常量池和字串常量池的區域分佈示意圖如下所示。
即字串常量池存在於堆中,並且如果要設定方法區大小,需要使用-XX:MaxMetaspaceSize=
指令進行設定。
JDK1.8中使用元空間作為方法區的實現以替代永久代(PermGen),有如下原因。
- 永久代作為方法區的實現時,字串常量池存在於執行時常量池中,即字串常量池存在於方法區中,而方法區只有
Full GC
時才會被清理,因此容易出現由於字串常量池導致的記憶體溢位; - 型別資訊等資料大小不容易確定,將其存放到本地記憶體更為合適。
二. 字串常量池
首先回答那個經典的問題:new
一個字串物件會建立幾個物件。答案是一個或者兩個。
字串常量池中會儲存字串字面量,字串字面量本質就是物件,當在程式碼中出現如下程式碼。
String str = "sakura";
如果字串常量池中已經存在sakura這個字串字面量,那麼str會指向字串常量池中的sakura字串字面量,反之,則會先將sakura這個字串字面量新增到字串常量池中,然後再將str指向字串常量池中的sakura字串字面量。
更甚一步,其實只要程式碼中出現雙引號括起來的字串,那麼就會去字串常量池中尋找對應的字串字面量,如果尋找不到,則建立字串字面量並新增到字串常量池中。
現在如果在程式碼中出現如下程式碼。
String str = new String("sakura");
首先出現了雙引號括起來的sakura字串,所以就會去字串常量池中尋找對應的字串字面量,如果尋找不到,則建立sakura字串字面量並新增到字串常量池中,如果尋找到,則直接使用字串常量池中的sakura字串字面量。最後,會在堆上建立一個字串物件,str會指向堆上建立出來的字串物件。所以new
一個字串物件時,可以肯定的是一定會在堆上建立一個字串物件,但是字串常量池中是否會建立一個字串字面量,要取決於字串字面量之前是否已經存在,已經存在則不會再重複建立。所以new
一個字串物件會建立幾個物件的答案是一個或者兩個。
為了加深理解,考慮如下的示例。
public class StringTest {
public static void main(String[] args) {
String str1 = new String("sakura") + new String("sakura"); //步驟1
String str2 = "sakurasakura"; //步驟2
System.out.println(str1 == str2); //步驟3
}
}
當執行完步驟1後,堆上的情況如下所示。
執行完步驟2後,堆上的情況如下所示。
所以最終步驟3的列印結果一定是false。
3. String的intern()方法
首先考慮如下的示例。
public class StringTest {
public static void main(String[] args) {
String str1 = new String("sakura") + new String("sakura"); //步驟1
str1.intern(); //步驟2
String str2 = "sakurasakura"; //步驟3
System.out.println(str1 == str2); //步驟4
}
}
上述示例和第2小節中的示例差不多,只不過多了一步str1.intern()
。String
的intern()
方法會根據當前字串物件的值去字串常量池中進行匹配,如果字串常量池中存在字串字面量的值與當前字串物件的值相等,則返回這個字串字面量的地址,如果字串常量池中不存在字串字面量的值與當前字串物件的值相等,則在字串常量池中註冊一個引用並指向當前字串物件,並最後返回當前字串物件的地址。那麼上述示例中,執行完步驟2後,堆上的情況如下所示。
執行完步驟3後,堆上的情況如下所示。
所以最終步驟4的列印結果一定是true。
透過上述示例可以知道,字串常量池中除了儲存字串字面量以外,還會儲存指向堆上的字串物件的引用。
總結
方法區用於存放JVM載入的型別資訊,常量,靜態變數和即時編譯器編譯後的程式碼快取等資料,JDK1.8中使用元空間作為方法區的實現,元空間使用的是本地記憶體。字串常量池中會儲存字串字面量,字串字面量本質就是物件,當new
一個字串物件時,一定會在堆上建立一個字串物件,但是字串常量池中是否會建立一個字串字面量,要取決於字串字面量之前是否已經存在,已經存在則不會再重複建立,所以new
一個字串物件會建立幾個物件的答案是一個或者兩個。