首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

JVM 理論:字符串常量池與 String.intern() 深度解析

     編輯:Java 綜合教學

JVM 理論:字符串常量池與 String.intern() 深度解析

本文通過 javap -verbose 反編譯字節碼,結合多個案例,深入剖析字符串常量池(String Constant Pool)、CONSTANT_String_info、CONSTANT_Utf8_info 結構,以及 intern() 的運行時行為。

常量池相關結構體

CONSTANT_String_info {
    u1 tag = 8;           // 標識字符串常量
    u2 string_index;      // 指向 CONSTANT_Utf8_info 的索引
}

CONSTANT_Utf8_info {
    u1 tag = 1;           // 標識 UTF-8 編碼字符串
    u2 length;            // 字節數組長度
    u1 bytes[length];     // UTF-8 編碼後的字節數組
}

案例 1:字面量賦值

public class StringDemo {
    private String s1 = "字符串S1";
}

javap -verbose 顯示:

  • #15 = Utf8 "字符串S1"(CONSTANT_Utf8_info)
  • #2 = String #15(CONSTANT_String_info,string_index=#15)

結論:字面量直接存入類常量池,引用指向 Utf8 結構。

案例 2:相同字面量多次引用

private String s1 = "MyStr1";
private String s2 = "MyStr1";

常量池中僅有一個 Utf8 "MyStr1" 和一個 String #index,s1 和 s2 在編譯期指向同一常量池條目,運行時 s1 == s2 為 true。

案例 3:new String("MyStr1")

private String s1 = new String("MyStr1");

字節碼顯示:

  1. 常量池有 Utf8 "MyStr1" 和 String #index。
  2. 構造器調用 java/lang/String."":(Ljava/lang/String;)V,在堆上創建新對象。

結果:s1 == "MyStr1" 為 false(堆對象 vs 常量池引用)。

案例 4:綜合對比(含 intern()、static、final、拼接)

public class StringDemo20 {
    private String s1 = "MyStr1";
    private String s2 = "MyStr1";
    private String s3 = "My" + "Str1";           // 編譯期常量折疊
    private String s01 = "My";
    private String s02 = "Str1";
    private String s4 = s01 + s02;               // 運行期拼接,new StringBuilder().append().toString()
    private String s5 = s4.intern();             // 手動入池

    private static String str1 = "MyStr1";
    private static String str2 = "MyStr1";
    private static String str3 = "My" + "Str1";
    private static String str01 = "My";
    private static String str02 = "Str1";
    private static String str4 = str01 + str02;
    private static String str5 = str4.intern();

    private static final String fstr1 = "MyStr1";
    private static final String fstr2 = "MyStr1";
    private static final String fstr3 = "My" + "Str1";
    private static final String fstr01 = "My";
    private static final String fstr02 = "Str1";
    private static final String fstr4 = fstr01 + fstr02;
    private static final String fstr5 = fstr4.intern();

    class InnerDemo { /* 類似字段 */ }

    public static void main(String[] args) {
        StringDemo20 d = new StringDemo20();
        // 大量 == 對比輸出...
    }
}

關鍵結論

表達式結果原因
s1 == s2true同一字面量,編譯期指向同一常量池條目
s1 == s3true"My"+"Str1" 編譯期常量折疊為 "MyStr1"
s1 == s4falses4 運行期 new StringBuilder 拼接,堆對象
s1 == s5trues4.intern() 將運行期字符串放入運行時常量池,返回池中引用
static 字段同理true/false規則同實例字段,靜態字段在類加載時初始化
final 字段true編譯期常量值傳播,等同字面量
final 引用變量拼接false非常量變量拼接,運行期執行

intern() 機制

  • JDK 6:常量池在 PermGen,intern() 將字符串複製到 PermGen,易 OOM。
  • JDK 7+:常量池移至 Heap,intern() 在池中記錄引用,不再複製字符數組,安全高效。
  • 調用 intern() 可將運行期動態生成的字符串「入池」,使後續相同內容字符串共享引用。

實戰建議

  1. 優先使用字面量或常量表達式,利用編譯期自動入池。
  2. 大量重複字符串(如解析 XML/JSON 產生的標籤名)可適度使用 intern() 節省內存。
  3. 避免在高頻路徑調用 intern()(有全局鎖開銷),考慮使用 ConcurrentHashMap 自建池。
  4. 注意 -XX:StringTableSize(默認 60013)調大以減少哈希衝突。

理解常量池結構與 intern() 語義,是診斷字符串內存問題、優化大規模文本處理的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved