jvm之棧、堆

2021-02-21 快學Java


人群當中,一位叫java的小夥子正向周圍一眾人群細數著自己取得的榮耀與輝煌。就在此時,c老頭和c++老頭緩步走來,看著被眾人圍住的java,c老頭感嘆地對著身旁的c++說道:「原以為你就可以挑起我的梁子一直走下去的。」 c++笑著回應道:「江山代有才人出,這世界以後總會是90後甚至00後的天下!」察覺到c和c++的java連忙走出人群,說道:「兩位前輩謙虛了,這世界可還離不開兩位前輩,我只不過是站在了兩位前輩的肩上罷了。」 「你這小子可是解決了我們不少的問題啊,像指針、多繼承、內存管理.那時,可是有很多程式設計師對我們抱怨頗深!」c++誇讚道。 「還有Java Virtual Machine,真的是一個不錯的想法!」一旁的c補充道。.Java虛擬機,一直都是都是我們在學習Java的過程中反覆提及的一個東西,那麼JVM具體是怎樣的呢?請看下圖:

2jvm.png

簡單說來,JVM的工作就是通過類加載系統將字節碼文件加載到內存當中去,加載到內存當中的數據,就從邏輯上形成了我們看到的圖中的運行時數據區(內存模型),

隨後執行引擎操作/調度內存模型中數據執行程序。

現在看到內存模型裡面的東西,大家是否有些眼熟呢?現在回想起自己面試時,遇到的JVM面試題是不是全是關於內存模型裡面的東西。比如:棧、堆、Eden、Survivor、GC等等。


public class Example{	public int add(){		int a = 3;		int b = 4;		int c = a + b;		return c;	}	public static void main(String[] args){		Example e1 = new Example();		e1.add();		//..	}	//.}

這個慄子是在幹啥,不用多說吧!今天我們就要來慢慢地剝開它,以往剝開吃得太快就沒有什麼感覺了。


棧,全稱為Java 虛擬機棧,線程私有,生命周期和線程一致。描述的是 Java 方法執行的內存模型:每個方法在執行時都會床創建一個棧幀(Stack Frame)用於存儲局部變量表、操作數棧、動態連結、方法出口等信息。每一個方法從調用直至執行結束,就對應著一個棧幀從虛擬機棧中入棧到出棧的過程。

怎樣解釋上面的話呢?那就需要開始剝慄子了!刀來(大喝一聲)!

當慄子開始執行時,由於只有一個main線程,因而JVM只需要為main線程分配好棧區的內存(話句話說如果有多個線程,自然就會有多個棧區,並且為各自線程私有)。OK! main繼續執行,就會遇到main()方法,遇到之後呢!JVM又會在棧區當中再劃出一個小塊來存放main()方法執行過程的數據,這一小塊區域也就是棧幀。main()方法執行過程中又有一個add()方法出現了,同樣地,JVM又會再為add()分配一個棧幀,同時壓入到棧區,以後再遇到其他方法也是如此。當然,方法在執行完成之後,便會彈出並釋放內存,當線程中棧區的所有方法都返回之後,程序也就算是執行完畢了。

那麼棧幀又是何許物業?咦,我刀呢?算了,手撕吧。

當我們扯開棧區,撕開棧幀,一不小心,局部變量表、操作數棧、動態連結、方法出口.譁啦啦地散落一地。

3zz.png

撿起add()棧幀的局部變量表和操作數棧就可以看到這樣一個畫面,在執行慄子中add()方法中的三行代碼時,局部變量表和操作數棧的一個變化過程:首先,執行int a = 3;局部變量表中會分配出一個int區域,表示為a;同時iconst命令使得操作數棧中壓入了常量3,然後再由istore命令將3彈出,賦值給局部變量表中a。同樣,int b = 4; 這一行代碼也是如此。然後,int c = a + b;從右往左開始,先執行a + b,也就是iload命令從局部變量中取出a、b對應的值,再將iadd後的值push進操作數棧中,剩下的便是int c = 7的操作了。

通過上面的慄子,就很容易明白;局部變量表,顧名思義就是存放每個方法中的局部變量(即編譯器可知的各種基本類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference 類型)和 returnAddress 類型(指向了一條字節碼指令的地址))所在處,如圖中的a、b。操作數棧,也就是存放的就是方法當中的各種操作數的臨時空間,又如慄子中的3、4。

動態連結:Class文件的常量池中存在有大量的符號引用,字節碼中的方法調用指令就以指向常量池的引用作為參數,而將部分符號引用在運行期間轉化為直接引用,這種轉化即為動態連結。這個解釋當中會涉及到許多概念,比如常量池、符號引用等,要想理解這些概念,就需要去了解class文件的結構,內容太多就不在這裡詳細描述了。

方法出口:簡單來說,就是用於標記當前方法執行完成之後,應該返回到下一條指令執行位置。比如就上面的慄子而言,add()在執行完畢之後,就應該返回到e1.add()之後繼續執行main()後面的代碼。


對於絕大多數應用來說,這塊區域是 JVM 所管理的內存中最大的一塊。線程共享,主要是用於存放對象實例和數組。除此之外,堆區還涉及到JVM中一個非常重要的工作--GC(Garbage Collection)。

4dui.png

從圖中就可以看出棧和堆之間的關係,對於new的對象,棧中局部變量表只會存放在堆中的地址引用,具體實例變量的空間分配都在堆中。

而堆中的內存區域又會劃分為年輕代和老年代兩部分,其中一般情況下年輕代佔1/3內存,老年代佔2/3內存;年輕代又被劃分為Eden區(伊甸園區)和兩個Survivor區(倖存區),各自分別佔年輕代空間的8/10、/1/10、1/10。也就是說如果堆內存區域有600M,那麼年輕代200M、老年代400M、Eden區160M、S0區20M、S1區20M。

這樣劃分區域的目的是什麼呢?這個回答就關係到JVM的GC機制了。

首先,程序一開始,所有的實例對象都會生成在Eden區中,當Eden區滿了的時候,這時就會觸發minor gc,jvm使用gc roots的查找方式將垃圾對象移動(複製算法)到S0區域中去,並且將Eden區中的其他對象視為垃圾對象,清空Eden區。

當實例對象再次充滿Eden區時,又會觸發minor gc;但是這次是將Eden區S0區中的所有非垃圾對象移動到S1中,並清空Eden區和S0區;同樣下次minor gc時,就是將Eden區和S1區中的非垃圾對象轉移到S0中.當然,這個左手倒右手過程並不是無休止的。在反覆minor gc的過程中,每個對象身上還有一個叫做分代年齡的屬性,每次minor gc對象的分代年齡就會加1,當達到15(默認情況)時,這個對象就會被放到老年代中去,成為長期存在的對象。除此之外,還有一種情況,即是當從Eden區複製內容到Survivor區時,複製內容大小超過S0或S1任一區域一半大小,也會直接被放入到老年代中,所以老年代才會需要那麼大的區域,不然怎麼抗得住這些年輕人這樣搞~~。

雖然老年代空間很大,但總會有滿了的時候,這時麻煩的事情就出現了——full gc。在full gc時,jvm會先觸發STW(Stop-The-World),也就是暫停其他所有的java進程,回收整個內存模型當中的內存資源,從而造成用戶響應超時或者是系統無響應,這對於並發程序比較高的系統(比如秒殺活動)影響程度是極其之大的。

通過gc機制,我們就可以得出一個簡單有效的JVM優化辦法,那就是減少full gc的次數,如何減少呢?只需要調整老年代和年輕代的內存空間分配使得在minor gc的過程中儘可能的消除大部分的垃圾對象。比如這種java -Xmx3072 -Xms3072M -Xmn2048M -Xss1M

-Xmx3072M:設置JVM最大可用內存為3072M。 -Xms3072M:設置JVM初始內存為3072M。此值可以設置與-Xmx相同,以避免每次垃圾回收完成後JVM重新分配內存。 -Xmn2048M:設置年輕代大小為2G。增大年輕代後,將會減小年老代大小。不過此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。 -Xss1M:設置每個線程的堆棧大小。JDK5.0以後每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。更具應用的線程所需內存大小進行調整。在相同物理內存下,減小這個值能生成更多的線程。

**GC Roots:**在上面的gc過程中,我們還提到了JVM是如何判斷垃圾對象的。簡單地來說,就是從gc roots的根出發(即局部變量表中的引用對象),一路沿著引用關係找,凡是能夠被找到的對象都是非垃圾對象,並且會被移動到下一個它應該去的區域中。剩下的對象,會在區域清空時,一同被清理掉而無須關心。


除了棧、堆之外,還有程序計數器、方法區(元空間)、本地方法棧,這些相對比較容易理解。

程序計數器:用來記錄當前指令執行完成後下一條指令的位置,由執行引擎來完成相應的修改操作。

方法區(元空間):存放常量、靜態變量、類信息等。

本地方法棧:與java虛擬機棧類似,不過存放的是native方法執行時的局部變量等數據存放位置。因為native方法一般不是由java語言編寫的,常見的就是**.dll**文件當中的方法(由C/C++編寫),比如Thread類中start()方法在運行時就會調用到一個start0()方法,查看源碼時就會看到private native void start0();這個方法就是一個本地方法。本地方法的作用就相當於是一個「接口」,用來連接java和其他語言的接口。

另外,對於new出來的對象,無論是在棧的局部變量表還是在方法區中的空間中,存放的都只是對象在中的地址(引用),具體的空間分配是在堆中。

相關焦點

  • 走進C語言:堆、棧與堆區、棧區,你知道有什麼區別嗎?
    一、區別 註:首先堆和棧可以分為兩種,一種是數據結構,另一種是和內存的分配有關,這兩種雖然都有棧和堆,但是兩者關係並不大, 1、棧、堆是數據結構裡面的叫法,注意:有時候有人喜歡這樣說 「堆棧」 其實說的就是棧而不是堆。
  • 這一定是全網寫JVM最好的文章之一—JVM運行時數據區
    為什麼說Java是一次編譯到處運行不管是windows,mac,還是linux,unix,oracle官網上都提供了下載對應的jdk版本,我們只需要編寫java代碼,因為jvm不同作業系統上下載的不同版本的,所以不需要我們管各種作業系統之間的區別,jvm只識別字節碼,所以jvm其實跟語言是解耦的,也沒有直接關聯。
  • 「GC系列」JVM堆內存分代模型及常見的垃圾回收器
    對象如何進行棧上分配C語言中的struct結構體就可以直接在棧上分配,在Java中也有棧上分配的理念。在JVM中,「堆是線程共享的」,因此堆上的對象對於各個線程都是共享和可見的,「只要持有對象的引用,就可以訪問堆中存儲的對象數據」。
  • 滿滿的一整篇,全是 JVM 核心知識點!
    分配空間後避免了在 Java 堆區跟 Native 堆中來回複製數據,可以有效提高讀寫效率,但它的創建、銷毀卻比普通 Buffer 慢。PS:如果使用了 NIO,本地內存區域會被頻繁的使用,此時 jvm 內存 ≈ 方法區 + 堆 + 棧+ 直接內存1.3、線程私有區域程序計數器、虛擬機棧、本地方法棧跟線程的聲明周期是一樣的。
  • 程序運行時,JVM內存到底是如何進行分配的?
    )1: istore_1 (把操作數棧棧頂的出棧放入局部變量表索引為 1 的位置)2: iconst_2 (把常量 2 壓入操作數棧棧頂)3: istore_2 (把操作數棧棧頂的出棧放入局部變量表索引為 2 的位置)4: iload_1 (把局部變量表索引為 1 的值放入操作數棧棧頂)5: iload_2 (把局部變量表索引為 2 的值放入操作數棧棧頂)6: iadd
  • 面試官:你說你熟悉jvm?那你講一下並發的可達性分析
    這次的文章我們聊聊jvm。jvm可以說是面試必備技能了。簡歷上寫了,多問幾句。簡歷上沒寫,也得提上幾句。我們先從一個簡單的熱身題入手,引出本文想要分享的內容。當面試扯到jvm這一部分的時候,面試官大概率會問你jvm怎麼判斷哪些對象應該回收呢?
  • 紹興山區的茶棧
    所謂「茶棧」,它們都是由一些中、小資本家集資創建的小型的、季節性的制茶廠。 工藝落後,設備簡陋,勞動強度大。但這些茶棧從它的經濟性質上分,則可分為「洋莊茶棧」和「土莊茶棧」兩種。陶星橋在王壇還創建了一個「萬羅」商場,專營洋雜百貨,如洋布洋紗,以及熱水瓶、搪磁器具等,茶農可以用茶葉交換自已所需之物,獲利頗豐。 土莊茶棧俗稱「小棧」,他們一則資本薄弱,二則又無政治上的靠山,小本經營,因此他們在茶葉採摘季節中不可能大量收購進毛茶,且還須向茶農賒購,所以他們精製一批茶葉只不過二、三百擔而已,自己還須參加勞動。
  • 周歲Party(小九心情驛棧)
    我想,小九對這份愛好的執著,以後還會把心情驛棧做到更加的完美。所以,我也會繼續的關注和支持小九心情驛棧,希望攜手小九與心情驛棧走得更穩更遠……眾心激情跨峰巔詩詞歌賦九州詠切磋曲藝再揚帆 九妹棧主音色甜美誦詩文價增添新詞古韻真情付儒雅蘭亭又重現 世界風情匯驛棧千姿百態賞不完奇思妙想裁精句吾輩取之學不全 幸入驛棧偷學藝恭恭敬敬拜眾賢
  • 【發現長春●悅來客棧】一百年前的「連鎖酒店」—悅來棧
    悅來棧的規模 自開業以來,悅來棧生意日漸紅火。可讓人沒想到的是,一場意外讓客棧遭受了滅頂之災。 1913年4月22日早上七點多,悅來棧突然失火。 失火兩周後,祖憲庭重整旗鼓,5月7日的《盛京時報》記載了重修的經過:「將後院之空房略加修整,暫時作為旅館運營,並決定在其前面修建樓房23間,瓦房八間,現在已經包定工程,不日即可動工興建
  • 寶安原創音樂劇《7號藝棧》首亮相
    音樂劇《7號藝棧》演出劇照。 寶安日報訊(記者 趙盼盼 通訊員 鄧裕達 文/圖)由寶安區文化廣電旅遊體育局與深圳市藝彩天成文化傳播有限公司聯袂打造的寶安原創音樂劇《7號藝棧》,近日在宗泰文創產業園亮相。
  • 可視化動圖帶你一步步講解棧有什麼用
    可視化動圖帶你一步步講解棧有什麼用 棧(stack)是限定僅在表尾(即棧頂)進行插入和刪除操作的線性表。對於棧來說,出棧只能將棧頂元素刪除。因此,執行一次出棧動作,就會刪除掉棧頂元素20。
  • 夢幻西遊三維版,鼎峙雲棧副本攻略最新版(附困難級)
    <夢幻西遊三維版>於2020年3月11日推出了新副本「鼎峙雲棧」,玩家小夥伴們渴望的各種新鮮內容紛至沓來、陸續更新了!鼎峙雲棧副本背景介紹:雲棧洞坐落於福陵山西北方向,原是豬八戒的洞府。在金蟬子隕落、取經人失去行蹤後,貪九鼎便來到雲棧洞佔山為王,在福陵鎮中跋扈一時,盤剝鄉裡。主線裡高紅蕊一事後,豬八戒曾親自料理雲棧洞,將作惡的豬妖趕跑。不料貪九鼎藉助九冥之力,捲土重來。作為正義的你,玩家隊伍在副本裡的任務就是打敗貪九鼎及其爪牙!
  • 基於和欣嵌入式作業系統實現的一個構件化的網絡協議棧設計淺析
    因此需要對龐大複雜的TCP/IP協議棧進行裁剪,使之具有簡單、高效的特點。同時在設計嵌入式TCP/IP協議棧時要合理控制中斷處理程序的大小,使得中斷處理程序的運行時間儘可能短;同時把那些無實時性要求和費時的工作移到主程序中執行,從而保證協議的正確執行。
  • 東莞市瓦藍棧公益基金會曬帳本,去年公益事業支出大於收入!
    根據瓦藍棧公益基金會曬出的帳本,去年,其捐贈收入共達2453379.26元。東莞市瓦藍棧公益基金會負責人孫岐坤表示,捐贈收入共來自幾個方面,其中的「大頭」是慕思·南都愛心基金的捐贈,共有170萬元,佔了所有收入的近7成。 「慕思·南都愛心基金相當於瓦藍棧公益基金會裡的一個專項基金。
  • 打造新中式精品酒店 | 訪北京皇家驛棧董事長劉少軍
    泳池建在陽臺上,眺望遠方清水中說起當年的故宮皇家驛棧,適逢2008年北京奧運會,酒店很短時間就獲得了很高的口碑,並登上了《福布斯》雜誌商務人士全球十二家最佳酒店的頭名位置。但是在一場盛大的北京奧運會後,美國爆發了金融危機,海外遊客銳減。之後,故宮皇家驛棧由於業主要收回,不得不放棄耗資千萬裝修的故宮皇家驛棧。
  • 省級文保單位生順茶棧完成修復 將開闢為紅色教育基地
    修復後的生順茶棧將被開闢為紅色教育基地。記者 吳暉 攝福州日報記者 吳暉福州市新一輪古厝提升行動持續推進——位於上下杭的省級文物保護單位生順茶棧近日完成修復,將闢為紅色教育基地。28日,記者隨負責指導該文保點修復的國家文物局古建築專家庫成員、古建築專家陳木霖實地探訪生順茶棧,探秘生順茶棧修復及其背後的紅色故事。文保點修復用上「釘子」生順茶棧位於下杭路238號,始建於清代,坐北朝南,共有三進,佔地面積2000多平方米。
  • 七牛雲姬長軍:企業技術棧向雲原生轉型的實踐與思考
    在 12 月 17 日下午的「雲原生領導力論壇」上,七牛雲業務效率負責人姬長軍帶來《企業技術棧向雲原生轉型的實踐與思考》的主題演講。事實也確實如此,雲原生涉及的技術棧較多,研發人員需要一定的時間去了解和實踐,因此在做技術棧升級時,技術領導者需要充分考慮到這一點。如何優雅地應對雲原生技術挑戰?
  • 婦聯喊你享民宿③丨【隱峰麓棧】十九峰旁如詩美景,尋覓畫布上的...
    HELLO SPRING春風十裡 ▪ 不如身旁有你2020年的春天裡,擁有更多美好的期待陰霾也藏匿不住向上的生機還有,只屬於春天的甜……停下腳步,洗淨鉛華在有天有地的院子裡賞四時之美回歸庭院生活只聞花香,不談悲喜找尋一個屬於自己的「世外桃源」加速產業復甦 激活旅遊消費婦聯喊你享民宿第3站走進新昌·隱峰麓棧新昌·隱峰麓棧地址:鏡嶺鎮雅莊村8號
  • 蟬來刻棧|魅力雕刻,將石頭融入五彩繽紛的動物世界
    二鼠 蟬來刻棧工作室 芙蓉石 2.1x2.1x5.9三靈猴獻壽 蟬來刻棧工作室 山秀園石 3.9x2.4x4.5雕刻時作者化繁為簡,象身碩大,以簡明的線條去表現深遠之意,旨在更顯造物的靈動和整體的和諧之美。兩童子各持其器,洗滌象身。大象神情慵懶愜意,童子身姿活潑,畫面生動。頗有清代印鈕之風,值得收藏。