學習JVM的時候常常會遇到各類常量池,不一樣版本的JDK它們的存儲位置也不一樣,這篇隨筆就整理下幾種常見的常量池,以JDK1.8爲主。先看一張存儲示意圖,裏面涉及1.8和1.6。java
app
public class Demo { public static void main(String[] args) { System.out.println("hello world"); } }
咱們對Demo.class「進行反彙編獲得具體字節碼信息,前面這一段是類的元信息,包括類名、修改時間、訪問修飾關鍵字等。學習
接着咱們能夠看到 Constant pool:,這就是常量池,每個符號後面引用了其餘符號或者表示具體的信息。其實常量池就是一張表,虛擬機指令根據這張常量表找到要執行的類名、方法名、參數類型、字面量等信息。ui
運行時常量池:是方法區的一部分,JDK1.8方法區位於系統內存中。當類被加載到內存時,那麼原先的常量池信息就會放入運行時常量池中,而且將 #1 這些符號地址變爲直接引用。spa
字符串常量池code
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裏。s二、s3邏輯相似,最終串池裏面的對象爲["a", "b", "ab"]。對象
blog
再看s5將兩個字符串常量進行拼接,這時候JVM並不會像拼接變量那樣建立對象,而是直接到串池中找到對象 「ab」,因此 「System.out.println(s3 == s5);」 輸出值爲true。這是由於 javac 在編譯期間認爲 "a" 、"b"是定值不會再改變, 因此直接獲得結果"ab"。 內存
其實咱們可使用 intern方法,主動將串池中尚未的字符串對象放入串池。字符串
可是若是在程序開始時就將「ab」放入串池,再比較 s == "ab「 就會返回false。