淺談一下Java中的堆和棧
Java數(shù)據(jù)類型在執(zhí)行過程中存儲(chǔ)在兩種不同形式的內(nèi)存中:棧和堆,它們通常由運(yùn)行Java虛擬機(jī)(JVM)的底層平臺(tái)維護(hù)。本文從Java軟件開發(fā)的角度提供了對(duì)這兩種內(nèi)存類型的一些見解。
Java程序是怎么運(yùn)行的
Java程序運(yùn)行在Java Virtual Machine (JVM)中,JVM提供了Java應(yīng)用程序在運(yùn)行時(shí)所需要的任何資源的管理器。這就意味著開發(fā)者寫的應(yīng)用程序或者創(chuàng)建的應(yīng)用程序沒有能力去直接獲取系統(tǒng)資源(不管是硬件還是軟件),除非JVM能提供給這些資源。所以在Java中,程序運(yùn)行順序如下圖:
JVM層使得Java平臺(tái)能夠獨(dú)立運(yùn)行,其他編程語(yǔ)言,例如C/C++沒有使用類似JVM層的東西,因此它們不是跨平臺(tái)的語(yǔ)言,即使它們是可移植的語(yǔ)言。它們就像下圖一樣:
這兩種形式有優(yōu)點(diǎn)也有缺點(diǎn),Java已經(jīng)有了自己的生態(tài)系統(tǒng)。與此同時(shí),像C/C++這樣的編程語(yǔ)言能夠直接訪問系統(tǒng)資源,從而更有利于優(yōu)化核心單元的使用,從而產(chǎn)生超級(jí)快速和高效的程序。但兩者在軟件開發(fā)領(lǐng)域都有各自的用途。
所有編程語(yǔ)言在編譯和執(zhí)行過程中都有許多相似之處。其中最重要的一點(diǎn)就是內(nèi)存管理,無(wú)論使用哪種語(yǔ)言,內(nèi)存管理對(duì)程序的整體效率都有重要影響,因?yàn)楣芾砗脙?nèi)存資源,從而才能管理好應(yīng)用程序性能。
Java中的運(yùn)行內(nèi)存
應(yīng)用程序之間的一個(gè)常見現(xiàn)象是,每個(gè)應(yīng)用程序都需要一些內(nèi)存才能以最佳方式工作,該內(nèi)存由底層平臺(tái)提供。在Java中,JVM提供了這些內(nèi)存資源(當(dāng)然需要操作系統(tǒng)授權(quán))。Java中,JVM內(nèi)存主要分為5個(gè)部分分別為:方法區(qū)、堆、棧、PC寄存器和本地方法區(qū)。
本文主要關(guān)注堆和棧。內(nèi)存不像一張白紙,程序員只需要草草記下就可以存儲(chǔ)數(shù)據(jù),在使用內(nèi)存之前,需要對(duì)其進(jìn)行結(jié)構(gòu)化。棧和堆是使用內(nèi)存時(shí)遵循的數(shù)據(jù)結(jié)構(gòu),在程序執(zhí)行期間,存儲(chǔ)的數(shù)據(jù)用于各種目的,這取決于程序的目的是什么。
JVM決定程序執(zhí)行期間使用的運(yùn)行時(shí)數(shù)據(jù)區(qū)域。有些數(shù)據(jù)區(qū)域是依賴于JVM的,這意味著它們是在JVM啟動(dòng)時(shí)創(chuàng)建的,并在JVM的整個(gè)生命周期中持續(xù)存在。但是,每個(gè)線程都創(chuàng)建和銷毀其他數(shù)據(jù)區(qū)域。JVM可以同時(shí)執(zhí)行多個(gè)執(zhí)行線程,這意味著每個(gè)線程都有自己的pc(Program Counter,程序計(jì)數(shù)器)來維護(hù)正在執(zhí)行的當(dāng)前指令的位置,還有一個(gè)棧幀來保存靜態(tài)內(nèi)存分配。
棧
棧是內(nèi)存中的一種結(jié)構(gòu),開發(fā)人員在其中存儲(chǔ)元素,其方式允許只從棧頂檢索數(shù)據(jù)——通常稱為先入后出(FILO或LIFO)。因?yàn)槊總€(gè)線程都維護(hù)一個(gè)私有的JVM棧,它被用來存儲(chǔ)與它們的靜態(tài)內(nèi)存分配相關(guān)的變量。特定于我們?cè)诖a中聲明和使用的方法的原語(yǔ)變量實(shí)際上存儲(chǔ)在棧區(qū)域中。另外,對(duì)實(shí)際存儲(chǔ)在堆內(nèi)存中的對(duì)象的引用也存儲(chǔ)在堆棧區(qū)域中。因此,任何本地分配的內(nèi)存都存儲(chǔ)在堆棧中。
堆棧內(nèi)存的默認(rèn)大小可以使用JVM參數(shù)-Xss來更改。有時(shí),如果分配了太多變量或方法遞歸調(diào)用自身,則堆??赡芤绯?。所有Java程序員都知道的一個(gè)常見錯(cuò)誤是Java.lang.stackoverflowerror
,當(dāng)棧內(nèi)存不足時(shí)提示該錯(cuò)誤。Java中的每個(gè)方法調(diào)用都會(huì)在棧中分配一塊內(nèi)存,因此,設(shè)計(jì)糟糕的遞歸方法調(diào)用很容易占用所有棧內(nèi)存,導(dǎo)致棧內(nèi)存溢出錯(cuò)誤。
堆
堆是JVM一啟動(dòng)就創(chuàng)建的內(nèi)存區(qū)域,它會(huì)一直存在,直到JVM被銷毀。與棧不同的是,棧是單個(gè)線程的屬性(因?yàn)槊總€(gè)線程都有自己的棧),堆實(shí)際上是由JVM本身管理的全局內(nèi)存,此內(nèi)存在運(yùn)行時(shí)用于為對(duì)象分配內(nèi)存。因此,對(duì)象的實(shí)例化可以是用戶定義的類、JDK或其他庫(kù)類。簡(jiǎn)而言之,使用new
關(guān)鍵字創(chuàng)建的任何對(duì)象都存儲(chǔ)在堆內(nèi)存中。堆內(nèi)存中的對(duì)象可被JVM運(yùn)行的所有線程訪問。訪問管理非常復(fù)雜,使用了非常復(fù)雜的算法,這就是JVM垃圾收集器發(fā)揮作用的地方。
堆的默認(rèn)大小可以使用JVM參數(shù)-Xms
和-Xmx
來更改。隨著對(duì)象的創(chuàng)建和銷毀,堆的大小也會(huì)增加或減少,如果達(dá)到最大內(nèi)存限制后并嘗試進(jìn)一步分配內(nèi)存,則拋出java.lang.OutOfMemoryError
。
堆中的字符串池(StringPool)
Java.lang.String
類是Java中使用最多的類,因此,應(yīng)該特別注意它的效率問題。與基本數(shù)據(jù)類型相比,字符串的操作效率總是很慢,所以,必須采用某種方式使得字符串對(duì)象操作的效率和便利性方面類似或者接近于基本數(shù)據(jù)類型,為了達(dá)到這個(gè)目的就在堆中分配了一塊特殊內(nèi)存區(qū)域(StringPool),創(chuàng)建的任何字符串對(duì)象都由JVM存儲(chǔ)在StringPool中。與堆中創(chuàng)建的其他對(duì)象相比,這提高了性能。
從代碼示例說明堆和棧
為了更好地說明在Java中堆和棧內(nèi)存的使用,讓我們寫一個(gè)簡(jiǎn)單的程序,并決定哪個(gè)分配分配到哪個(gè)內(nèi)存——堆或棧:
public class HeapAndStackTest { public static void main(String[] args) { int x=10; int y=20; String greet = "Hello"; Date d = new Date(); diff(x, y); } public static int diff(int x1, int x2) { return x2-x1; } }
這段代碼運(yùn)行方式如下:
- 程序啟動(dòng),JVM將Java運(yùn)行時(shí)環(huán)境(JRE)類加載到堆中。
- 在遇到
main()
方法時(shí),會(huì)創(chuàng)建一個(gè)棧幀。 - 局部變量
x
和y
存儲(chǔ)在棧中。 - 字符串
greet
分配在堆的StringPool區(qū)域中。 Date
對(duì)象分配在堆區(qū),而它的引用d
存儲(chǔ)在棧中。
總結(jié)
棧和堆是Java程序在代碼執(zhí)行期間使用的兩個(gè)區(qū)域。除了這兩個(gè)之外,還有其他內(nèi)存區(qū)域,如方法區(qū)域、寄存器、本地方法域等等。每種區(qū)域在Java應(yīng)用程序中都有其特定的用途。但是,從程序員的角度來看,棧和堆是JVM必須理解的區(qū)域。
到此這篇關(guān)于淺談一下Java中的堆和棧的文章就介紹到這了,更多相關(guān)Java的堆和棧內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java發(fā)送form-data請(qǐng)求實(shí)現(xiàn)文件上傳的示例代碼
最近做一個(gè)需求,需要請(qǐng)求第三方接口上傳文件,該請(qǐng)求類型是form-data請(qǐng)求,本文就來介紹一下java發(fā)送form-data請(qǐng)求實(shí)現(xiàn)文件上傳的示例代碼,感興趣的可以了解一下2023-12-12Java AQS中ReentrantReadWriteLock讀寫鎖的使用
ReentrantReadWriteLock稱為讀寫鎖,它提供一個(gè)讀鎖,支持多個(gè)線程共享同一把鎖。這篇文章主要講解一下ReentrantReadWriteLock的使用和應(yīng)用場(chǎng)景,感興趣的可以了解一下2023-02-02聊一聊jdk1.8中的ArrayList 底層數(shù)組是如何擴(kuò)容的
這篇文章主要介紹了聊一聊jdk1.8中的ArrayList 底層數(shù)組是如何擴(kuò)容的,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08SpringCloud中的Stream服務(wù)間消息傳遞詳解
這篇文章主要介紹了SpringCloud中的Stream服務(wù)間消息傳遞詳解,Stream 就是在消息隊(duì)列的基礎(chǔ)上,對(duì)其進(jìn)行封裝,可以是我們更方便的去使用,Stream應(yīng)用由第三方的中間件組成,應(yīng)用間的通信通過輸入通道和輸出通道完成,需要的朋友可以參考下2024-01-01SpringBoot解析指定Yaml配置文件的實(shí)現(xiàn)過程
這篇文章主要介紹了SpringBoot解析指定Yaml配置文件,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03