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