首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Java 記憶體模型深度:字串常量池 vs 基本型別棧儲存,相同值是否共享記憶體?

     編輯:程式設計疑難解答

經典面試題:`String s1="123"; String s2="123";` 棧中只佔一塊記憶體 (常量池共享);那麼 `int i1=1; int i2=1;` 呢?本文從 JVM 記憶體結構、編譯器優化、運行時行為三個維度徹底釐清。

結論先行

類型代碼記憶體分配是否共享核心原因
String (引用型別)`String s1="123"; String s2="123";`**堆** (對象) + **棧** (引用) + **常量池** (字面量)**是** (引用指向同一常量池字面量)編譯期確定字面量,類加載時進入常量池,`ldc` 指令推同一引用
int (基本型別)`int i1=1; int i2=1;`**棧** (局部變量表槽位)**否** (兩個獨立槽位,各存 32 位整數 1)基本型無引用概念,直接存值;編譯器不會合併局部變量槽位
Integer (包裝類)`Integer i1=1; Integer i2=1;`堆 (對象) + 棧 (引用) + 常量池緩存 (-128~127)**是** (自動拆箱調用 `Integer.valueOf(1)` 返回緩存對象)JDK 源碼 `IntegerCache` 緩存小整數,`valueOf` 優先返回緩存

深度解析:為什麼 `int` 不共享?

1. JVM 棧幀結構

每個方法調用建立一個**棧幀**,包含**局部變量表**。`i1`、`i2` 是兩個獨立的 **Slot (槽位,32 位)**,各自存儲 `0x00000001`。編譯器生成的字節碼:

iconst_1       // 推入常量 1
istore_1       // 存入局部變量 1 (i1)
iconst_1       // 再次推入常量 1
istore_2       // 存入局部變量 2 (i2)

兩條 `istore` 寫入不同索引,**物理上就是兩塊獨立記憶體區域**。JVM 規範**不要求**合併相同基本型別值的槽位 (不像常量池合併字面量)。

2. 基本型別 vs 引用型別的本質差異

  • 基本型別:按值存儲,無身份 (Identity),無引用,生命週期綁定棧幀。`i1 == i2` 比較的是**值**。
  • 引用型別:棧存引用 (地址),堆存對象。`s1 == s2` 比較**地址**,`s1.equals(s2)` 比較**內容**。字串字面量因不可變特性,JVM 採用**享元模式**在常量池共享同一對象。

3. 編譯器優化視角

JIT 編譯器可能將極熱點路徑的局部變量提升到寄存器,甚至常量傳播消除變量,但這是**運行時優化**,不改變語言語義層面的「兩個獨立變量」。語言規範保證 `i1`、`i2` 可獨立修改 (雖然基本型別不可變,但槽位可重寫),故必須分配獨立存儲。

延伸:`final` 與常量折疊

final int a = 1;
final int b = 1;
// 編譯器常量折疊:a+b 在編譯期直接算為 2,字節碼無加法指令
// 但 a、b 在局部變量表仍佔兩個槽位 (除非優化消除)

總結

**字串常量池是 JVM 為不可變字面量設計的特殊共享區**;**基本型別直接在棧槽位存值,無共享機制**。理解「值存儲 vs 引用存儲」、「常量池 vs 局部變量表」的本質區別,才能準確預測記憶體行為,避免在性能調優、對象比較、並發編程中踩坑。

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