Java中常量池、堆和棧的區(qū)別對比與聯(lián)系
在 Java 中,常量池、堆、棧是 JVM 內(nèi)存模型中三個核心的內(nèi)存區(qū)域,各自承擔(dān)不同的職責(zé),其位置、存儲內(nèi)容和特性有顯著區(qū)別,但又相互關(guān)聯(lián)。下面詳細(xì)解析:
一、基本概念與存儲內(nèi)容
1. 堆(Heap)
- 位置:JVM 中最大的內(nèi)存區(qū)域,屬于線程共享區(qū)域。
- 存儲內(nèi)容:
所有對象實例(包括通過new創(chuàng)建的對象、數(shù)組)和字符串常量池(JDK7 及之后) 都存儲在這里。
例如:new Object()、new int[10]、new String("abc")創(chuàng)建的對象,以及 JDK7 + 的字符串常量池。 - 特點:
- 內(nèi)存動態(tài)分配,大小不固定,可隨程序運行擴(kuò)展。
- 由垃圾回收器(GC)管理內(nèi)存回收,當(dāng)對象無引用時會被回收。
- 訪問速度較慢(相比棧)。
2. 棧(虛擬機棧,VM Stack)
- 位置:線程私有,每個線程創(chuàng)建時會分配一個獨立的棧,與線程生命周期一致。
- 存儲內(nèi)容:
以棧幀(Stack Frame) 為單位存儲,每個方法調(diào)用時會創(chuàng)建一個棧幀,包含:- 局部變量表(方法內(nèi)的局部變量,如
int a = 1; String s;); - 操作數(shù)棧(方法執(zhí)行時的臨時數(shù)據(jù)操作);
- 方法返回地址(方法執(zhí)行完畢后回到調(diào)用處的地址)等。
例如:main方法調(diào)用時,會生成一個棧幀,其中的String s(局部變量)就存儲在局部變量表中。
- 局部變量表(方法內(nèi)的局部變量,如
- 特點:
- 內(nèi)存大小固定(可通過 JVM 參數(shù)配置),遵循 “先進(jìn)后出”(FILO)原則。
- 方法執(zhí)行結(jié)束后,棧幀自動銷毀,內(nèi)存無需 GC 干預(yù),效率極高。
- 訪問速度快(相比堆),因為棧是連續(xù)的內(nèi)存空間。
3. 常量池(Constant Pool)
常量池并非單一區(qū)域,而是一個 “存儲常量的集合”,細(xì)分為Class 常量池、運行時常量池、字符串常量池,其位置和作用不同:
| 類型 | 位置(JDK8+) | 存儲內(nèi)容 |
|---|---|---|
| Class 常量池 | .class 文件中(加載后進(jìn)入元空間) | 類編譯時生成的常量,如字面量(字符串、數(shù)字)、符號引用(類名、方法名)等。 |
| 運行時常量池 | 元空間(本地內(nèi)存,方法區(qū)實現(xiàn)) | Class 常量池加載到內(nèi)存后的表現(xiàn)形式,常量在此處被解析為直接引用(如對象地址)。 |
| 字符串常量池 | 堆內(nèi)存 | 存儲字符串字面量(如"abc"),用于復(fù)用相同內(nèi)容的字符串,減少內(nèi)存消耗。 |
- 核心特點:
- 存儲的都是 “常量”(編譯期或運行期確定的不變值),如字符串字面量、基本類型常量(
final int a = 10)等。 - 字符串常量池是最常被討論的,例如
String s = "abc"中,"abc"會被放入字符串常量池,后續(xù)相同字面量會直接復(fù)用。
- 存儲的都是 “常量”(編譯期或運行期確定的不變值),如字符串字面量、基本類型常量(
二、三者的區(qū)別
| 維度 | 堆(Heap) | 棧(VM Stack) | 常量池(以字符串常量池為例) |
|---|---|---|---|
| 存儲內(nèi)容 | 對象實例、數(shù)組、字符串常量池(JDK7+) | 局部變量、棧幀(方法調(diào)用信息) | 字符串字面量、基本類型常量等 |
| 線程共享性 | 線程共享(所有線程可訪問同一對象) | 線程私有(每個線程有獨立的棧) | 字符串常量池線程共享;Class 常量池隨類加載,線程共享 |
| 內(nèi)存管理 | 由垃圾回收器(GC)回收 | 隨線程 / 方法結(jié)束自動釋放(棧幀彈出) | 字符串常量池中的常量在無引用時被 GC 回收 |
| 內(nèi)存大小 | 大(可動態(tài)擴(kuò)展) | ?。ü潭?,易棧溢出) | 中等(依賴常量數(shù)量) |
| 訪問速度 | 慢(內(nèi)存不連續(xù),需 GC 管理) | 快(內(nèi)存連續(xù),無 GC 干預(yù)) | 較快(復(fù)用機制減少創(chuàng)建開銷) |
| 生命周期 | 隨對象引用存在而存在 | 隨線程或方法調(diào)用周期存在 | 隨類加載 / 常量創(chuàng)建而存在,無引用時銷毀 |
三、三者的聯(lián)系
三個區(qū)域并非孤立,而是通過 “引用” 相互關(guān)聯(lián),共同支撐 Java 程序的運行:
棧 → 堆:棧中的局部變量(引用類型)指向堆中的對象。
例如:String s = new String("abc")中,s是棧中的局部變量,指向堆中new String("abc")創(chuàng)建的對象。
堆 → 字符串常量池:堆中的字符串對象可能引用字符串常量池中的字面量。
- 例如:
String s = "abc"中,堆中可能創(chuàng)建一個 String 對象(若常量池?zé)o"abc"),該對象引用字符串常量池中的"abc";后續(xù)String s2 = "abc"會直接復(fù)用常量池中的"abc",堆中無需重復(fù)創(chuàng)建。 - 棧 → 常量池:棧中的局部變量可直接引用常量池中的常量。
- 例如:
final String s = "abc"中,s(棧中)直接引用字符串常量池中的"abc"。 - 方法調(diào)用時的協(xié)作:
- 調(diào)用方法時,棧中創(chuàng)建棧幀(存儲局部變量),局部變量若為引用類型,則指向堆中的對象或常量池中的常量;方法執(zhí)行中需要的常量(如字符串)從運行時常量池獲取。
四、舉例說明三者關(guān)系
public class MemoryDemo {
public static void main(String[] args) {
// 1. "hello"放入字符串常量池(堆中);
// s1(棧中)引用常量池中的"hello"
String s1 = "hello";
// 2. 堆中創(chuàng)建新對象(內(nèi)容為"hello"),該對象引用常量池中的"hello";
// s2(棧中)指向堆中的新對象
String s2 = new String("hello");
int a = 10; // 3. a是局部變量,直接存儲在棧的局部變量表中
}
}s1(棧)→ 字符串常量池(堆)中的"hello";s2(棧)→ 堆中的new String對象 → 引用字符串常量池中的"hello";a(棧)直接存儲值10(基本類型,無需堆或常量池)。
五、字符串常量池與堆內(nèi)存
重要判斷技巧:
- 雙引號 ("")里的內(nèi)容, 都會存放在常量池中
- new 出來的對象都在堆內(nèi)存 ;此時,堆中的對象與字符串常量池?zé)o關(guān)
- 只要是new的對象,都是唯一的。
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s3 == s4); // false
}總結(jié)
- 堆是對象的 “倉庫”,存儲所有動態(tài)創(chuàng)建的實例;
- 棧是方法執(zhí)行的 “工作臺”,存儲局部變量和調(diào)用信息,速度快但空間有限;
- 常量池是 “常量緩存區(qū)”,存儲不變值以復(fù)用,減少內(nèi)存浪費。
到此這篇關(guān)于Java中常量池、堆和棧的區(qū)別與聯(lián)系的文章就介紹到這了,更多相關(guān)java常量池堆和棧內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java 數(shù)據(jù)結(jié)構(gòu)基本算法希爾排序
這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)基本算法希爾排序的相關(guān)資料,需要的朋友可以參考下2017-08-08
8個簡單部分開啟Java語言學(xué)習(xí)之路 附j(luò)ava學(xué)習(xí)書單
8個簡單部分開啟Java語言學(xué)習(xí)之路,附j(luò)ava學(xué)習(xí)書單,這篇文章主要向大家介紹了學(xué)習(xí)java語言的方向,感興趣的小伙伴們可以參考一下2016-09-09
Mybatis學(xué)習(xí)總結(jié)之mybatis使用建議
這篇文章主要介紹了Mybatis學(xué)習(xí)總結(jié)之mybatis使用建議的相關(guān)資料,非常具有參考借鑒價值,需要的朋友可以參考下2016-05-05
Java調(diào)用參數(shù)類型是application/x-www-form-urlencoded的API問題
在使用Postman進(jìn)行接口測試時,對于POST請求,需將請求頭設(shè)置為application/x-www-form-urlencoded,并將參數(shù)轉(zhuǎn)為String類型,通常在GET請求中,參數(shù)直接拼接在URL后,本文通過具體實例,詳細(xì)講解了參數(shù)處理的方法,適合API開發(fā)者參考2024-09-09
Springboot登錄驗證的統(tǒng)一攔截處理的實現(xiàn)
如果不進(jìn)行統(tǒng)一的攔截處理,每次用戶請求你都要去進(jìn)行用戶的信息驗證,所以本文主要介紹了Springboot登錄驗證的統(tǒng)一攔截處理的實現(xiàn),感興趣的可以了解一下,感興趣的可以了解一下2023-09-09

