Java中常量池、堆和棧的區(qū)別對(duì)比與聯(lián)系
在 Java 中,常量池、堆、棧是 JVM 內(nèi)存模型中三個(gè)核心的內(nèi)存區(qū)域,各自承擔(dān)不同的職責(zé),其位置、存儲(chǔ)內(nèi)容和特性有顯著區(qū)別,但又相互關(guān)聯(lián)。下面詳細(xì)解析:
一、基本概念與存儲(chǔ)內(nèi)容
1. 堆(Heap)
- 位置:JVM 中最大的內(nèi)存區(qū)域,屬于線程共享區(qū)域。
- 存儲(chǔ)內(nèi)容:
所有對(duì)象實(shí)例(包括通過(guò)new創(chuàng)建的對(duì)象、數(shù)組)和字符串常量池(JDK7 及之后) 都存儲(chǔ)在這里。
例如:new Object()、new int[10]、new String("abc")創(chuàng)建的對(duì)象,以及 JDK7 + 的字符串常量池。 - 特點(diǎn):
- 內(nèi)存動(dòng)態(tài)分配,大小不固定,可隨程序運(yùn)行擴(kuò)展。
- 由垃圾回收器(GC)管理內(nèi)存回收,當(dāng)對(duì)象無(wú)引用時(shí)會(huì)被回收。
- 訪問(wèn)速度較慢(相比棧)。
2. 棧(虛擬機(jī)棧,VM Stack)
- 位置:線程私有,每個(gè)線程創(chuàng)建時(shí)會(huì)分配一個(gè)獨(dú)立的棧,與線程生命周期一致。
- 存儲(chǔ)內(nèi)容:
以棧幀(Stack Frame) 為單位存儲(chǔ),每個(gè)方法調(diào)用時(shí)會(huì)創(chuàng)建一個(gè)棧幀,包含:- 局部變量表(方法內(nèi)的局部變量,如
int a = 1; String s;); - 操作數(shù)棧(方法執(zhí)行時(shí)的臨時(shí)數(shù)據(jù)操作);
- 方法返回地址(方法執(zhí)行完畢后回到調(diào)用處的地址)等。
例如:main方法調(diào)用時(shí),會(huì)生成一個(gè)棧幀,其中的String s(局部變量)就存儲(chǔ)在局部變量表中。
- 局部變量表(方法內(nèi)的局部變量,如
- 特點(diǎn):
- 內(nèi)存大小固定(可通過(guò) JVM 參數(shù)配置),遵循 “先進(jìn)后出”(FILO)原則。
- 方法執(zhí)行結(jié)束后,棧幀自動(dòng)銷毀,內(nèi)存無(wú)需 GC 干預(yù),效率極高。
- 訪問(wèn)速度快(相比堆),因?yàn)闂J沁B續(xù)的內(nèi)存空間。
3. 常量池(Constant Pool)
常量池并非單一區(qū)域,而是一個(gè) “存儲(chǔ)常量的集合”,細(xì)分為Class 常量池、運(yùn)行時(shí)常量池、字符串常量池,其位置和作用不同:
| 類型 | 位置(JDK8+) | 存儲(chǔ)內(nèi)容 |
|---|---|---|
| Class 常量池 | .class 文件中(加載后進(jìn)入元空間) | 類編譯時(shí)生成的常量,如字面量(字符串、數(shù)字)、符號(hào)引用(類名、方法名)等。 |
| 運(yùn)行時(shí)常量池 | 元空間(本地內(nèi)存,方法區(qū)實(shí)現(xiàn)) | Class 常量池加載到內(nèi)存后的表現(xiàn)形式,常量在此處被解析為直接引用(如對(duì)象地址)。 |
| 字符串常量池 | 堆內(nèi)存 | 存儲(chǔ)字符串字面量(如"abc"),用于復(fù)用相同內(nèi)容的字符串,減少內(nèi)存消耗。 |
- 核心特點(diǎn):
- 存儲(chǔ)的都是 “常量”(編譯期或運(yùn)行期確定的不變值),如字符串字面量、基本類型常量(
final int a = 10)等。 - 字符串常量池是最常被討論的,例如
String s = "abc"中,"abc"會(huì)被放入字符串常量池,后續(xù)相同字面量會(huì)直接復(fù)用。
- 存儲(chǔ)的都是 “常量”(編譯期或運(yùn)行期確定的不變值),如字符串字面量、基本類型常量(
二、三者的區(qū)別
| 維度 | 堆(Heap) | 棧(VM Stack) | 常量池(以字符串常量池為例) |
|---|---|---|---|
| 存儲(chǔ)內(nèi)容 | 對(duì)象實(shí)例、數(shù)組、字符串常量池(JDK7+) | 局部變量、棧幀(方法調(diào)用信息) | 字符串字面量、基本類型常量等 |
| 線程共享性 | 線程共享(所有線程可訪問(wèn)同一對(duì)象) | 線程私有(每個(gè)線程有獨(dú)立的棧) | 字符串常量池線程共享;Class 常量池隨類加載,線程共享 |
| 內(nèi)存管理 | 由垃圾回收器(GC)回收 | 隨線程 / 方法結(jié)束自動(dòng)釋放(棧幀彈出) | 字符串常量池中的常量在無(wú)引用時(shí)被 GC 回收 |
| 內(nèi)存大小 | 大(可動(dòng)態(tài)擴(kuò)展) | ?。ü潭ǎ讞R绯觯?/td> | 中等(依賴常量數(shù)量) |
| 訪問(wèn)速度 | 慢(內(nèi)存不連續(xù),需 GC 管理) | 快(內(nèi)存連續(xù),無(wú) GC 干預(yù)) | 較快(復(fù)用機(jī)制減少創(chuàng)建開(kāi)銷) |
| 生命周期 | 隨對(duì)象引用存在而存在 | 隨線程或方法調(diào)用周期存在 | 隨類加載 / 常量創(chuàng)建而存在,無(wú)引用時(shí)銷毀 |
三、三者的聯(lián)系
三個(gè)區(qū)域并非孤立,而是通過(guò) “引用” 相互關(guān)聯(lián),共同支撐 Java 程序的運(yùn)行:
棧 → 堆:棧中的局部變量(引用類型)指向堆中的對(duì)象。
例如:String s = new String("abc")中,s是棧中的局部變量,指向堆中new String("abc")創(chuàng)建的對(duì)象。
堆 → 字符串常量池:堆中的字符串對(duì)象可能引用字符串常量池中的字面量。
- 例如:
String s = "abc"中,堆中可能創(chuàng)建一個(gè) String 對(duì)象(若常量池?zé)o"abc"),該對(duì)象引用字符串常量池中的"abc";后續(xù)String s2 = "abc"會(huì)直接復(fù)用常量池中的"abc",堆中無(wú)需重復(fù)創(chuàng)建。 - 棧 → 常量池:棧中的局部變量可直接引用常量池中的常量。
- 例如:
final String s = "abc"中,s(棧中)直接引用字符串常量池中的"abc"。 - 方法調(diào)用時(shí)的協(xié)作:
- 調(diào)用方法時(shí),棧中創(chuàng)建棧幀(存儲(chǔ)局部變量),局部變量若為引用類型,則指向堆中的對(duì)象或常量池中的常量;方法執(zhí)行中需要的常量(如字符串)從運(yùn)行時(shí)常量池獲取。
四、舉例說(shuō)明三者關(guān)系
public class MemoryDemo {
public static void main(String[] args) {
// 1. "hello"放入字符串常量池(堆中);
// s1(棧中)引用常量池中的"hello"
String s1 = "hello";
// 2. 堆中創(chuàng)建新對(duì)象(內(nèi)容為"hello"),該對(duì)象引用常量池中的"hello";
// s2(棧中)指向堆中的新對(duì)象
String s2 = new String("hello");
int a = 10; // 3. a是局部變量,直接存儲(chǔ)在棧的局部變量表中
}
}s1(棧)→ 字符串常量池(堆)中的"hello";s2(棧)→ 堆中的new String對(duì)象 → 引用字符串常量池中的"hello";a(棧)直接存儲(chǔ)值10(基本類型,無(wú)需堆或常量池)。
五、字符串常量池與堆內(nèi)存
重要判斷技巧:
- 雙引號(hào) ("")里的內(nèi)容, 都會(huì)存放在常量池中
- new 出來(lái)的對(duì)象都在堆內(nèi)存 ;此時(shí),堆中的對(duì)象與字符串常量池?zé)o關(guān)
- 只要是new的對(duì)象,都是唯一的。
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é)
- 堆是對(duì)象的 “倉(cāng)庫(kù)”,存儲(chǔ)所有動(dòng)態(tài)創(chuàng)建的實(shí)例;
- 棧是方法執(zhí)行的 “工作臺(tái)”,存儲(chǔ)局部變量和調(diào)用信息,速度快但空間有限;
- 常量池是 “常量緩存區(qū)”,存儲(chǔ)不變值以復(fù)用,減少內(nèi)存浪費(fèi)。
到此這篇關(guān)于Java中常量池、堆和棧的區(qū)別與聯(lián)系的文章就介紹到這了,更多相關(guān)java常量池堆和棧內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot如何在線程中獲取@Service Bean類
這篇文章主要介紹了SpringBoot如何在線程中獲取@Service Bean類,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
Eclipse Debug模式的開(kāi)啟與關(guān)閉問(wèn)題簡(jiǎn)析
這篇文章主要介紹了Eclipse Debug模式的開(kāi)啟與關(guān)閉問(wèn)題簡(jiǎn)析,同時(shí)向大家介紹了一個(gè)簡(jiǎn)單的debug模式啟動(dòng)不起來(lái)的解決方法,希望對(duì)大家有所幫助。2017-10-10
Java實(shí)現(xiàn)的打地鼠小游戲完整示例【附源碼下載】
這篇文章主要介紹了Java實(shí)現(xiàn)的打地鼠小游戲,結(jié)合完整實(shí)例形式分析了Java多線程操作及鍵盤按鍵響應(yīng)實(shí)現(xiàn)的打地鼠游戲功能相關(guān)操作技巧,需要的朋友可以參考下2018-07-07
SpringCloud分布式事務(wù)Seata部署和集成過(guò)程
這篇文章主要介紹了SpringCloud分布式事務(wù)Seata部署和集成過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-10-10
java中URLEncoder.encode與URLDecoder.decode處理url特殊參數(shù)的方法
這篇文章主要給大家介紹了關(guān)于java中URLEncoder.encode與URLDecoder.decode處理url特殊參數(shù)的方法,文中介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-03-03
如何使用IntelliJ IDEA中的Live Templates自定義代碼模板
在IntelliJ IDEA中,通過(guò)使用LiveTemplates功能,可以實(shí)現(xiàn)快速編碼和自定義代碼模板,例如,輸入“main”可以自動(dòng)補(bǔ)全主函數(shù)結(jié)構(gòu),“sout”可以補(bǔ)全輸出語(yǔ)句,用戶可以通過(guò)設(shè)置中的LiveTemplates選項(xiàng)查看和定義快捷模板,支持使用分組管理和參數(shù)化模板內(nèi)容,適應(yīng)復(fù)雜的編碼需求2024-11-11
java商城項(xiàng)目實(shí)戰(zhàn)之購(gòu)物車功能實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了java商城項(xiàng)目實(shí)戰(zhàn)之購(gòu)物車功能實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04

