Hot Spot虛擬機新生代爲什麼是一個eden+2個survivor

注:本文針對Hot Spot虛擬機

一、分代收集

在很多時候,JVM中對象的生命週期差距較大,部分對象可能是“朝生夕死”的(大部分),而部分對象可能又是比較“命長”的(小部分)。所以根據對象生命週期的特點,我們將堆空間分爲幾個區域,比如新生代、老年代,在不同的分代可以採取不同的收集算法,以最大化效率。

二、新生代與複製算法

不考慮特殊情況,對象會優先分配到新生代,並且對象大多都是朝生夕死的,每次新生代GC都會有大批的對象死去,只有少量存活,所以新生代一般採取“複製算法”來進行垃圾收集。

複製算法的思想就是將內存空間劃分爲大小相等的兩部分,每次只使用其中一塊,當這塊內存不足以容納新對象的時候,就將存活的對象複製到另外一塊上,然後將前一塊內存空間中使用的內存一次性清理掉,這樣在內存分配時就不用考慮內存碎片的情況了,既簡單又高效。

三、一個eden和一個survivor

既然採取複製算法,那我們就需要把新生代內存空間進行劃分。但是在新生代中的對象大多數都是短命的,如果將新生代劃分爲大小相等的兩部分的話,就太浪費內存空間了,所以即使我們需要劃分新生代空間,也並不需要按照1:1的標準來。既然大多數對象都是短命的,那麼我們按照一個大空間、一個小空間來進行劃分就好了嘛,這最好再增加一個可配置參數,讓用戶可以根據實際情況來進行調整。對象優先分配到大的那塊空間,經過一次GC之後,只有少數的對象存活,我們將這些存活的對象複製到小的那塊空間,正好合適。

假設我們稱大的那塊空間叫做eden,小的那塊叫做survivor。如果新生代就是這種內存佈局,能解決我們的問題嗎?

首先對象優先分配到eden,當eden空間不能再容納新對象的時候,觸發一次Mnior GC,將eden中還存活的對象複製到survivor中(假設這會兒survivor放得下),然後直接清理eden中使用過的內存空間,這沒有什麼問題。

但是如果eden空間再次不足,又觸發Minor GC呢?此時survivor中同時存在存活的對象和死亡的對象(需要被清理),並且存活的對象可能還沒有到進入老年代的年齡,它們還需要留在新生代。我們需要清理survivor中死亡的對象,保留存活的對象,並且將eden中存活的對象複製到survivor中,然後清理eden。那麼該如何操作呢?

首先,我們可以使用標記清理算法來清理survivor中死亡對象所佔的內存空間,然後將eden中存活的對象複製到survivor中的空閒位置(現在暫時不用考慮空間不足的情況)。但需要注意的是,此時survivor中的這些空閒空間並不是連續的,也就是說可能會有很多內存碎片,這種情況下在其中分配內存會非常麻煩。

當然,爲了避免這種麻煩,我們可以使用標記整理算法,將survivor中所有存活的對象都向一端移動,然後直接清理掉邊界以外的內存,這樣空閒空間也就連續了,複製eden存活對象的時候就可以直接移動堆頂指針,按序放入,不用再考慮前面內存碎片的問題。但不可避免的,我們多了整理survivor空間的操作。

四、一個eden和兩個survivor

考慮到前面描述的一個eden和一個survivor的情況會有較大的性能損耗,我們嘗試再劃分出一塊內存空間來解決這個問題,這塊內存空間的作用是什麼呢?

GC時,我們可以直接將eden和survivor空間中存活的對象複製到這個新劃分的內存空間中,然後清理eden和survivor的內存空間,這時它們的可用內存都規整了,下個週期又轉換survivor和這個新空間的角色,周而復始,對象可能會在survivor和新空間中來回移動,而每個對象都有自己的GC年齡,年齡到的時候進入老年代就行了。這樣是不是高效多了?

這個新空間也是用於容納經歷過GC還存活的對象,所以也可以稱之爲survivor,並且並不需要很大的內存空間,我們將其定義爲和survivor一樣的大小,同時爲了作爲區分,一個叫做from survivor,一個叫做to survivor,並且它們是等效的。基於上述流程,其中一個survivor始終是空的,假設eden和兩個survivor的比列爲 8:1:1,那麼其實我們也只浪費了10%的新生代空間。

那如果我們再分幾塊survivor空間,性能會不會更好呢?明顯不是的,從我們前面的分析來看,再增加survivor並不能解決什麼實際的問題,還會徒增空間維護成本,就有點畫蛇添足的味道了。

五、總結

簡單的來說,出於性能考慮,分代收集是合理的,同時根據大多數對象短命的特點,新生代採取複製算法同樣有利於在實際場景中提高性能。而複製算法的高效,需要至少一塊空的內存空間(規整),這樣在複製對象的時候纔不需要考慮內存碎片的影響,也才能體現其性能。但是新生代如果只使用一塊eden和一塊survivor,是不能保證沒有內存碎片影響的,除非經歷過一次GC之後,survivor中存活的對象直接進入老年代,但是這樣分代收集又沒有什麼意義了。

所以說現在這種空間劃分,包括分代收集,其實都是出於性能考慮,不這樣做其實也能實現功能。當然,性能肯定是越高越好。我們在思考的時候不要太在意每個區域是什麼名字,更不要死記硬背,重要的是理解它的工作方式和存在的意義。

注:本文是博主的個人理解,如果有錯誤的地方,希望大家不吝指出,謝謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章