Java虛擬機回憶錄

Java虛擬機 編程語言 Java團長 2017-08-03

最近蠻閒,無代碼可擼,在北京地鐵程序員專線上看著一哥們兒在看周大神的書,腦海就開始回憶一下當時看那本書時,對它的理解。那首歌唱的好:“你的心有一道牆”,相信很多人在從事Java不久都會對JVM有種神祕感以及對那些人的膜拜,“他為啥那麼厲害,臥槽,他對JVM好熟,好屌好屌”。

那麼今天我就來談談我對他的理解。

Java虛擬機回憶錄

首先,每當說起JVM大概都會想到Java虛擬機運行時數據區,那麼它是怎樣劃分的呢?話不多說,別跟一個直男程序員談理論扯理想,最愛看的就是圖(圖摘自互聯網)。

很多人粗略的將它分為堆區和非堆區,由上圖可以看出Java虛擬機在執行程序時會把他管理的內存劃分為不同的數據區。

首先第一個想到的就是它了,在虛擬機啟動時就創建,一坨兒活躍在虛擬機所管理內存中的巨無霸(內存最大),被所有線程共享的內存區域,該區域唯一的目的就是存放對象實例,幾乎所有的對象實例以及數組都要在堆上分配,堆是虛擬機收集的主要區域,基本上都採用的是分代收集算法,分為:新生代、老年代,堆大小可通過-Xms、-Xmx控制,如果堆沒有內存來完成實例分配且也無法再擴展時,將會OOM。

新生代:新創建的對象在此分配,由於新生代中的對象屬於朝生夕死的,其回收算法採用的是複製算法,它由Eden區和兩個大小相等的From Survivor區、To Survivor區構成。當Minor GC時,Eden區存活的對象將被移到To Survivor區,而From Survivor區存活的對象根據其年齡決定,當年齡達到閥值(-XX:MaxTenuringThreshold可配,沒記錯的話默認15歲就老了)則會進入老年代,否則進入To Survivor區,虛擬機會清空Eden區和From Survivor區,最後將To Survivo區與From Survivor區互換角色來保證To Survivo區為空。

Java虛擬機回憶錄

這裡只說下部分調整年輕代的配置,其它的大家可根據Sun文檔選擇合適自己的。

1)-XX:NewSize、-XX:MaxNewSize 設置年輕代的大小,建議兩個值設為一樣大,一般為整個堆大小的1/3或者1/4。

2)-XX:SurvivorRatio 設置Eden和Survivor的比值,默認8:1:1。

3)-XX:+PrintTenuringDistribution 用於顯示每次Minor GC時Survivor區中各個年齡段的對象的大小。

4)-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold用於設置晉升到老年代的對象年齡的最小值和最大值,每扛過一次Minor GC之後,年齡就加1。

5)-Xmn 設置年輕代大小 可代替1) 。整個JVM內存大小=年輕代大小 + 年老代大小 + 持久代大小。持久代一般固定大小為64m,所以增大年輕代後,將會減小年老代大小。此值對系統性能影響較大,官方推薦配置為整個堆的3/8。

老年代:存放的對象比較穩定,主要存放著那些扛過了好幾次Minor GC回收仍然還活著或者特別大(XX:PretenureSizeThreshold 可配)的對象,當老年代的連續空間無法分配給新進入的較大對象時,會觸發一次Full GC來騰出更多的空間,Full GC的代價很高,會造成Stop the world,所以要減少Full GC次數。老年代GC採用的是標記-清除或者標記-整理算法(根據收集器選擇)。

標記-清除算法:Mark-Sweep顧名思義算法分為標記和清除兩個步驟,虛擬機會先標記出所有需要回收的對象,在標記完成後統一回收它們。標記過程大概是:對對象進行可達性分析後,發現木有與GC Roots相連接的引用鏈,將會第一次被標記並會搞一次篩選活動,活動內容就是看看這個對象有沒有必要執行finalize方法,如果沒有覆蓋這個方法或者已經執行過這個方法,那麼虛擬機會認為沒有必要去執行。如果虛擬機認為有必要執行這個方法的話,這個對象就會去F-Queue裡待著等待一個finalizer線程去執行finalize方法,因為這個方法是脫離死亡的救命草;第二次標記是GC對F-Queue進行小規模標記,如果對象在finalize方法抓住了那根草(與GC Roots引用鏈上的任何一個對像關聯即可),那麼它將被移除即將回收的區域,如果沒有抓住那根草那麼它基本靠別自行車了。標記-清除算法有兩個不足點,第一就是它的兩個過程效率都不高,另一個就是空間問題,會產生大量不連續的內存碎片,會導致分配較大對象時無法申請到足夠的連續空間從而觸發一次GC。

Java虛擬機回憶錄

複製算法:它的出現就是為了解決標記清除的不足,套路就是將內存劃分為兩個等量大小的塊兒,對象都在其中一塊兒上,當這一塊兒造完了就將存活的對象複製到另一塊兒上,然後將剛剛那塊兒一次清理掉,這樣就不需要考慮內存碎片問題,動動指針按順序非配就搞定了,實現簡單效率高,但是代價有點大內存直接幹了一半,適用於對象存活率低的區域,比如朝生夕死的新生代。

Java虛擬機回憶錄

標記-整理算法:複製算法看起來很吊,但是對於對象存活率高的區域就顯得力不從心了,而且如果不想浪費一半的空間的話,就需要進行空間分配擔保(抵押貸款),所以老年代不能這麼搞,進而出現了標記-整理算法,套路跟標記-清除一樣,只是不直接清理可回收的對象,而是存活的往一邊兒移動,然後根據分界線去幹掉另一邊兒,可以看出該算法要進行對象的移動,成本相對略高,但好處則是不會產生內存碎片。

Java虛擬機回憶錄

方法區

方法區多數人認為的永久代,方法區與堆一樣是線程共享的內存區域,類使用要經過加載、連接(驗證、準備、解析)和初始化,加載後的類信息就存在方法區特定的數據結構中,主要包括:類的全路徑名包括超類(如果這個類是Object則它沒有超類)、類的類型、類的訪問修飾符、直接接口全限定名的有序列表、運行時常量池(類版本、字段、方法信息、常量、類靜態變量、裝載器信息) 等等。由於線程都共享方法區,所以方法區的數據必須時線程安全的,如果有2個甚至多個線程同時訪問某個類,而這類又沒被JVM加載,那麼JVM只允許一個線程去加載(雙親委派),其它線程必須等待。方法區的內存不一定是連續的,可以動態擴展大小,可以選擇不實現GC,GC的目標主要是常量池的回收和類型的卸載,所以想想就好沒多少便宜可撿,因為回收條件比較苛刻,當方法區無法滿足內存分配需求時將OOM(String.intern()是個好例子)。

廣告時間:

ps:臥槽,好久沒寫了,感覺比擼代碼還難!PM,給我一個需求我還你一個美好的明天,放心,不加班。

上面說完了線程共享區域,接來下逼叨逼叨線程隔離數據區。

程序計數器

程序計數器屬於線程私有的,它是當前線程所執行字節碼的指示器(執行到那兒了),它是一塊較小的內存空間,線程下一步該幹撒就是通過字節碼解釋器改變計數器來執行的,每個線程都有自己的程序計數器,多線程就是輪流切換它來實現,Java方法記錄的是虛擬機字節碼指令地址,Native方法沒有記錄,程序計數器在JVM中是唯一一個沒有定義OOM的區域。

虛擬機棧

如程序計數器一樣,Java虛擬機棧也屬於線程私有,所以它的生命週期與線程一樣。它屬於Java方法執行的內存模型,每個方法執行都會創建一個棧幀,主要存儲著方法出口信息、局部變量表、操作數棧、動態鏈接。當線程請求的棧幀深度大於虛擬機所允許的深度會SOF,若虛擬機棧動態擴展時無法申請到足夠的內存會OOM。

方法出口信息:正常方法返回時可能需要在棧幀中保存一些信息,用來幫助恢復它的上層方法的執行狀態,如果有返回值,則把它壓入調用者棧幀的操作數棧中,調整計數器的值以指向方法調用指令後面的一條指令,若方法異常退出,那麼返回地址是通過異常處理器來確定的,棧幀中一般不會保存這部分信息。

局部變量表:所需的內存空間在編譯期確定,一旦確定無法更改大小,它存放著編譯期的各種基本數據類型、reference類型(可能是對象引用指針,也可能是個句柄)、returnAddress類型(指向某條字節碼指令的地址)。

操作數棧:棧幀剛創建時,操作數棧是沒有數據的,當執行方法操作時,會存放從局部變量表複製的常量或者變量,包括方法入參和返回值,操作數棧都一個固定的棧深度,入棧按先進後出方式,最大深度由編譯期確定,基本類型除了long,double用2個深度,其他都用一個。

動態鏈接:class的常量池中存在有大量的符號引用,字節碼中的方法調用指令就以常量池中指向方法的符號引用為參數,這些符號引用分為兩種,一種就是類加載的時候,靜態解析的那些final 和static代碼塊,得到的直接引用,還有一種是運行期間轉化的(每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用),這種就是動態鏈接。

本地方法棧

跟虛擬機棧的作用是一個屌樣,唯一區別就是虛擬機棧是為字節碼服務的,而它是為Native方法服務,與虛擬機棧一樣,當線程請求的棧幀深度大於虛擬機所允許的深度會SOF,若虛擬機棧動態擴展時無法申請到足夠的內存會OOM。

直接內存

Direct Memory 雖然不屬於虛擬機運行數據區,但在被NIO引入後一直頻繁使用(比如堆外緩存),可以用Native方法直接分配堆外內存,然後在堆中去引用這塊兒區域(DirectByteBuffer就是),如果動態擴展內存時達到物理內存限制會OOM。

內存分配策略以及類加載機制以後再補,先寫到這兒吧,未完待續!

學習Java的同學注意了!!!

學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入Java學習交流群495273252,我們一起學Java!

相關推薦

推薦中...