JVM內存區域劃分 EDEN SPACE、SURVIVOR SPACE、TENURED GEN

本文轉自:點擊打開鏈接


JVM區域總體分兩類,heap區和非heap區。heap區又分:Eden Space(伊甸園)、Survivor Space(倖存者區)、Tenured Gen(老年代-養老區)。 非heap區又分:Code Cache(代碼緩存區)、Perm Gen(永久代)、Jvm Stack(java虛擬機棧)、Local Method Statck(本地方法棧)。

HotSpot虛擬機GC算法採用分代收集算法:

1、一個人(對象)出來(new 出來)後會在Eden Space(伊甸園)無憂無慮的生活,直到GC到來打破了他們平靜的生活。GC會逐一問清楚每個對象的情況,有沒有錢(此對象的引用)啊,因爲GC想賺錢呀,有錢的纔可以敲詐嘛。然後富人就會進入Survivor Space(倖存者區),窮人的就直接kill掉。

2、並不是進入Survivor Space(倖存者區)後就保證人身是安全的,但至少可以活段時間。GC會定期(可以自定義)會對這些人進行敲詐,億萬富翁每次都給錢,GC很滿意,就讓其進入了Genured Gen(養老區)。萬元戶經不住幾次敲詐就沒錢了,GC看沒有啥價值啦,就直接kill掉了。

3、進入到養老區的人基本就可以保證人身安全啦,但是億萬富豪有的也會揮霍成窮光蛋,只要錢沒了,GC還是kill掉。

分區的目的:新生區由於對象產生的比較多並且大都是朝生夕滅的,所以直接採用標記-清理算法。而養老區生命力很強,則採用複製算法,針對不同情況使用不同算法。

非heap區域中Perm Gen中放着類、方法的定義,jvm Stack區域放着方法參數、局域變量等的引用,方法執行順序按照棧的先入後出方式。

現代JVM內存管理方法及GC的實現和主要思路

謹以此文紀念已經辭世的C語言之父,Dennis Ritchie。無論世事如何變遷,無論日月如何更替,您的光輝成就都照耀着現代計算機技術發展之路。

提到現代JVM內存管理,就不能不提到一個意義深遠的東西,C語言。C語言最爲人詬病,但是也是C語言最讓人神往的,就是它的內存管理機制。在C語言中,程序員可以自由的控制內存,自己決定內存裏寫0還是寫1.所謂的數據類型轉換,在C語言看來,不過就是內存裏的幾次複製以及排列位置的不同,僅此而已。

然而隨着應用規模的不斷增大,無論是盤根錯節的對象耦合關係,還是巨大的內存使用量,都讓開發人員麻爪。動輒幾個GB的內存總量,動輒成千上萬的內存對象數量,都不再是一個人乃至十個人可以控制的範圍了。況且,百密一疏,只要有一點點內存泄露,隨着時間的推移,都有可能變成無比的災難。OOM之類的問題,在程序員眼裏,早已經是家常便飯,誰還沒溢出過內存呢,是吧。

的確是有高手可以控制好內存,但是不是所有人。那麼,大規模團隊化開發的時候,如何保證內存使用不出現問題呢?代碼走查?人工校驗?反覆測試?這些能不能行的通先不談,就算可行,巨大的工作量也可以讓所有合同超期到下個世紀。於是有人提出了一個想法。可以不可以讓一部分高手寫出完善的內存管理模塊,再加上一堆各式各樣的類庫和標準,最後構成一個龐大的運行時?

這一想法被無數語言團隊採用。第一個實現的,就是James Gosling領導的Java團隊。Java的目標是Write Once,Run Anywhere.估計他們在咖啡館喝咖啡的時候一時寫錯了,應該是Debug Anywhere,這才符合現在的實際,呵呵。扯遠了,我們回頭看內存管理。

JVM提供了很多類庫,封裝了很多數據類型和常用工具類,作爲自己的基本庫來使用,比如java.lang包。舉一個最簡單的例子,來一句最簡單的代碼。int i = 5;

在C語言裏,這句話申請了幾個字節的內存,然後放了個5進去,Java也是這麼搞的。只不過,C語言裏申請了以後要自己管理,而Java你不用自己煩惱這個事情,虛擬機會幫你處理。它會判斷何時需要,何時不需要。由此推開去,更加複雜的業務,比如連接數據庫,讀取文件,我們要做的只是調用類庫而已,內存申請和釋放都由虛擬機全盤接管,我們不用動一根手指頭。

我們是爽了,虛擬機就頭疼了。這麼多對象,什麼時候該銷燬,什麼時候該保持,什麼時候要檢查這些關係呢?在JVM裏,這個事情有一個模塊來做,也就是我們這片文章的主角,GC,Garbage Collection,垃圾回收。

假設我們是實現GC的程序員,那麼我們要做什麼呢?首先,負責分配內存,負責控制對象的持有計數,負責銷燬內存對象,還得負責內存整理什麼的。在Sun制定的JVM規範裏,詳細描述了GC部分要做的事情,這裏就不贅述了,想看的話,請自行Google。

現有的JVM,主流的,分別是HotSpot和JRockit,主要研究對象也是這兩個。這篇文章裏,我們只研究HotSpot,也就是所謂的Sun JVM。目前階段,Sun的GC方式主要有CMS和G1兩種。考慮到效果和實際應用,這裏只介紹CMS。

CMS,全稱Concurrent Low Pause Collector,是JDK1.4後期版本開始引入的新gc算法,在jdk5和jdk6中得到了進一步改進,它的主要適合場景是對響應時間的重要性需求較高的應用,並且預期這部分應用能夠承受垃圾回收線程和應用線程共享處理器資源,且應用中存在比較多的長生命週期的對象的應用。CMS是用於對tenured generation的回收,也就是年老代的回收,目標是儘量減少應用的暫停時間,減少full gc發生的機率,利用和應用程序線程併發的垃圾回收線程來標記清除年老代。

JVM在程序運行過程當中,會創建大量的對象,這些對象,大部分是短週期的對象,小部分是長週期的對象,對於短週期的對象,需要頻繁地進行垃圾回收以保證無用對象儘早被釋放掉,對於長週期對象,則不需要頻率垃圾回收以確保無謂地垃圾掃描檢測。爲解決這種矛盾,Sun JVM的內存管理採用分代的策略。

1)年輕代(Young Gen):年輕代主要存放新創建的對象,內存大小相對會比較小,垃圾回收會比較頻繁。年輕代分成1個Eden Space和2個Suvivor Space(命名爲A和B)。當對象在堆創建時,將進入年輕代的Eden Space。垃圾回收器進行垃圾回收時,掃描Eden Space和A Suvivor Space,如果對象仍然存活,則複製到B Suvivor Space,如果B Suvivor Space已經滿,則複製到Old Gen。同時,在掃描Suvivor Space時,如果對象已經經過了幾次的掃描仍然存活,JVM認爲其爲一個持久化對象,則將其移到Old Gen。掃描完畢後,JVM將Eden Space和A Suvivor Space清空,然後交換A和B的角色(即下次垃圾回收時會掃描Eden Space和BSuvivor Space。這麼做主要是爲了減少內存碎片的產生。

我們可以看到:Young Gen垃圾回收時,採用將存活對象複製到到空的Suvivor Space的方式來確保儘量不存在內存碎片,採用空間換時間的方式來加速內存中不再被持有的對象儘快能夠得到回收。
2)年老代(Tenured Gen):年老代主要存放JVM認爲生命週期比較長的對象(經過幾次的Young Gen的垃圾回收後仍然存在),內存大小相對會比較大,垃圾回收也相對沒有那麼頻繁(譬如可能幾個小時一次)。年老代主要採用壓縮的方式來避免內存碎片(將存活對象移動到內存片的一邊,也就是內存整理)。當然,有些垃圾回收器(譬如CMS垃圾回收器)出於效率的原因,可能會不進行壓縮。
3)持久代(Perm Gen):持久代主要存放類定義、字節碼和常量等很少會變更的信息。


發佈了30 篇原創文章 · 獲贊 27 · 訪問量 67萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章