學習JVM的時候經常會遇到各種常量池,不同版本的JDK它們的儲存位置也不同,這篇隨筆就整理下幾種常見的常量池,以JDK1.8為主。先看一張儲存示意圖,裡面涉及1.8和1.6。
public class Demo { public static void main(String[] args) { System.out.println("hello world"); } }
我們對Demo.class“進行反彙編得到具體位元組碼資訊,前面這一段是類的元資訊,包括類名、修改時間、訪問修飾關鍵字等。
接著我們可以看到 Constant pool:,這就是常量池,每一個符號後面引用了其他符號或者表示具體的資訊。其實常量池就是一張表,虛擬機器指令根據這張常量表找到要執行的類名、方法名、引數型別、字面量等資訊。
執行時常量池:是方法區的一部分,JDK1.8方法區位於系統記憶體中。當類被載入到記憶體時,那麼原先的常量池資訊就會放入執行時常量池中,並且將 #1 這些符號地址變為直接引用。
字串常量池
public class Demo { public static void main(String[] args) { String s1 = "a"; String s2 = "b"; String s3 = "ab"; String s4 = s1 + s2; System.out.println(s3 == s4); // false String s5 = "a" + "b"; System.out.println(s3 == s5); // true } }
編譯這段程式的時候a、b、ab這些都是執行時常量池中的符號,還沒變成Java 字串物件。當執行到“String s1 = "a"; ”這行程式碼時,ldc會將a符號變為 “a” 字串物件,如果串池中沒有"a"物件,“a”物件就會被放入StringTable裡。s2、s3邏輯類似,最終串池裡面的物件為["a", "b", "ab"]。
再看s5將兩個字串常量進行拼接,這時候JVM並不會像拼接變數那樣建立物件,而是直接到串池中找到物件 “ab”,所以 “System.out.println(s3 == s5);” 輸出值為true。這是因為 javac 在編譯期間認為 "a" 、"b"是定值不會再改變, 所以直接得到結果"ab"。
其實我們可以使用 intern方法,主動將串池中還沒有的字串物件放入串池。
但是如果在程式開始時就將“ab”放入串池,再比較 s == "ab“ 就會返回false。