Java內(nèi)存劃分和堆棧的簡單整理

一直不理解堆棧的概念,很多視頻課程講解又不全面,學習代碼的數(shù)據(jù)結構和算法成為了我最多困惑的地方。最近閑暇,查了下資料,基于Java語言整理下內(nèi)存區(qū)域劃分的基礎知識。
由于Java程序是交由JVM執(zhí)行的,所以在談Java內(nèi)存區(qū)域劃分的時候事實上是指JVM內(nèi)存區(qū)域劃分。在討論JVM內(nèi)存區(qū)域劃分之前,先來看一下Java程序具體執(zhí)行的過程:


Java程序執(zhí)行過程

運行時數(shù)據(jù)區(qū)模塊

了解jvm的結構之前,有必要先來了解一下操作系統(tǒng)的內(nèi)存基本結構


操作系統(tǒng)存儲體系

以上是操作系統(tǒng)存儲層次【CPU <--- > 寄存器<--- > 緩存(最多三級緩存)<--- >內(nèi)存<--- >磁盤緩存<--- >固定磁盤存儲<--- >可移動存儲介質】的部分展示。
操作系統(tǒng)內(nèi)存布局:
操作系統(tǒng)內(nèi)部布局

操作系統(tǒng)內(nèi)存的堆棧:

棧區(qū)(stack):由編譯器自動分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結構中的棧。
堆區(qū) (heap):一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數(shù)據(jù)結構中的堆是兩回事,分配方式倒是類似于鏈表

棧是為執(zhí)行線程留出的內(nèi)存空間(當線程創(chuàng)建的時候,操作系統(tǒng)會為每個系統(tǒng)級的線程分配棧);
棧頂會為局部變量和數(shù)據(jù)預留塊,當函數(shù)執(zhí)行完畢,塊就沒有用了,可能在下次的函數(shù)調用的時候再被使用,棧通常都是采用后進先出的方式預留空間;因此最近的保留塊通常最先被釋放,從棧中釋放塊不過是指針的偏移(這里和數(shù)據(jù)結構中的棧的意思類似)

堆的數(shù)據(jù)結構并不是由系統(tǒng)支持的,而是由函數(shù)庫提供的基本的malloc/realloc/free函數(shù)維護了一套內(nèi)部的堆數(shù)據(jù)結構(所以才需要程序員自己釋放內(nèi)存,否則會造成內(nèi)存泄漏);

堆包含了一個鏈表來維護已用和空閑的內(nèi)存塊;
申請內(nèi)存:
當程序需要在堆中分配內(nèi)存的時候,會從內(nèi)部堆中尋找可用的內(nèi)存空間,通過鏈表找到符合大小的內(nèi)存塊(鏈表遍歷的方向是由低地址指向高地址),但是由于堆是不連續(xù)的內(nèi)存區(qū)域,當找不到合適的內(nèi)存區(qū)域的時候,則會利用系統(tǒng)調用來動態(tài)增加程序數(shù)據(jù)段的內(nèi)存大小;
釋放內(nèi)存:
當系統(tǒng)受到程序的釋放內(nèi)存的申請的時候,會遍歷該鏈表,尋找第一個空間大于所申請的堆結點,然后該結點會從鏈表中刪除,并將該結點的空間釋放給內(nèi)存,這片內(nèi)存空間又會返回到堆結構中,會經(jīng)過內(nèi)存塊的組合,以便適合下次內(nèi)存分配申請;(這里面如果沒有管理內(nèi)存分配在釋放內(nèi)存時很容易會造成內(nèi)存碎片)

操作系統(tǒng)中的jvm

為什么jvm的內(nèi)存是分布在操作系統(tǒng)的堆中呢??因為操作系統(tǒng)的棧是操作系統(tǒng)管理的,它隨時會被回收,所以如果jvm放在棧中,那java的一個null對象就很難確定會被誰回收了,那gc的存在就一點意義都沒有了,而要對棧做到自動釋放也是jvm需要考慮的,所以放在堆中就最合適不過了。

操作系統(tǒng)+jvm的內(nèi)存簡單布局

JVM 的內(nèi)存主要分為3個分區(qū)

堆區(qū)(Heap)-- 只存對象(數(shù)組)本身(引用類型的數(shù)據(jù)),不存基本類型和對象的引用。JVM只有一個堆區(qū),這個“堆”是動態(tài)內(nèi)存分配意義上的堆——用于管理動態(tài)生命周期的內(nèi)存區(qū)域。JVM的堆被同一個JVM實例中的所有Java線程共享,它通常由某種自動內(nèi)存管理機制所管理,這種機制通常叫做“垃圾回收”(garbage collection,GC)。JVM規(guī)范并不強制要求JVM實現(xiàn)采用哪種GC算法。
棧區(qū)(Stack)-- 棧中只保存基礎數(shù)據(jù)類型的對象和對象引用。每個線程一個棧區(qū),每個棧區(qū)中的數(shù)據(jù)都是私有的,其他棧不能訪問。棧內(nèi)有幀(方法調用會生成棧幀)分三個部分:基本類型變量區(qū),執(zhí)行環(huán)境上下文,操作指令區(qū)。
方法區(qū) -- 又叫靜態(tài)區(qū),跟堆一樣,被所有線程共享。方法區(qū)包含所有的class和static變量。方法區(qū)包含的都是在整個程序中永遠唯一的元素。如:class,satic。


運行時jvm的處理

細化到增加jvm內(nèi)部的處理和pc寄存器的配合時,可見,無論是在虛擬機中還是在我們虛擬機所寄宿的操作系統(tǒng)中功能目的是一致的,計算機上的pc寄存器是計算機上的硬件,本來就是屬于計算機,計算機用pc寄存器來存放“偽指令”或地址,而相對于虛擬機,pc寄存器它表現(xiàn)為一塊內(nèi)存(一個字長,虛擬機要求字長最小為32位),虛擬機的pc寄存器的功能也是存放偽指令,更確切的說存放的是將要執(zhí)行指令的地址,它甚至可以是操作系統(tǒng)指令的本地地址,當虛擬機正在執(zhí)行的方法是一個本地方法的時候,jvm的pc寄存器存儲的值是undefined,所以應該很明確的知道,虛擬機的pc寄存器是用于存放下一條將要執(zhí)行的指令的地址(字節(jié)碼流)。

當一個classLoder啟動的時候,classLoader的生存地點在jvm中的堆,然后它會去主機硬盤上將A.class裝載到jvm的方法區(qū),方法區(qū)中的這個字節(jié)文件會被虛擬機拿來new A字節(jié)碼(),然后在堆內(nèi)存生成了一個A字節(jié)碼的對象,然后A字節(jié)碼這個內(nèi)存文件有兩個引用一個指向A的class對象,一個指向加載自己的classLoader,如下圖。


image.png

方法區(qū)中的字節(jié)碼內(nèi)存塊,除了記錄一個class自己的class對象引用和一個加載自己的ClassLoader引用之外,還記錄了以下信息。


image.png

從上面的圖,不難發(fā)現(xiàn),原來jvm的設計的模型其實就是操作系統(tǒng)的模型,基于操作系統(tǒng)的角度,jvm就是個java.exe/javaw.exe,也就是一個應用,而基于class文件來說,jvm就是個操作系統(tǒng),而jvm的方法區(qū),也就相當于操作系統(tǒng)的硬盤區(qū),所以他也叫permanent區(qū),因為這個單詞是永久的意思,也就是永久區(qū),我們的磁盤就是不斷電的永久區(qū)。而java棧和操作系統(tǒng)棧是一致的,無論是生長方向還是管理的方式,至于堆,雖然概念上一致目標也一致,分配內(nèi)存的方式也一直(new,或者malloc等等),但是由于他們的管理方式不同,jvm是gc回收,而操作系統(tǒng)是程序員手動釋放,所以在算法上有很多的差異。

堆棧的概念有2種,通常除了內(nèi)存上面的概念,還有數(shù)據(jù)結構里面的概念,兩者不完全相同。簡單的概念區(qū)分如下:

棧(操作系統(tǒng)-內(nèi)存):由編譯器自動分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結構中的棧, 他們通常都是被調用時處于存儲空間中,調用完畢立即釋放
堆(操作系統(tǒng)-內(nèi)存): 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收,分配方式倒是類似于鏈表。生命周期由虛擬機的垃圾回收算法來決定(并不是一旦成為孤兒對象就能被回收)。所以調用這些對象的速度要相對來得低一些
堆(數(shù)據(jù)結構):堆可以被看成是一棵樹,如:堆排序
棧(數(shù)據(jù)結構):一種后進先出的數(shù)據(jù)結構

參考資料:
JVM與操作系統(tǒng)
https://zhuanlan.zhihu.com/p/44401058
操作系統(tǒng)中堆和棧的區(qū)別
https://blog.csdn.net/SpeedMe/article/details/22943191
java之jvm學習筆記十三(jvm基本結構)
https://blog.csdn.net/yfqnihao/article/details/8289363
JVM的內(nèi)存區(qū)域劃分
https://www.cnblogs.com/dolphin0520/p/3613043.html
操作系統(tǒng)之堆和棧的區(qū)別
https://www.cnblogs.com/George1994/p/6399895.html
JVM學習(2)——技術文章里常說的堆,棧,堆棧到底是什么,從os的角度總結
https://www.cnblogs.com/kubixuesheng/p/5202561.html

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內(nèi)容