大話Java堆的分區Eden、From Survivor、To Survivor、老年代

Java虛擬機運行時數據區一般分爲:程序計數器、Java虛擬機棧、本地方法棧、Java堆、方法區。
在這裏插入圖片描述

在《Java虛擬機規範》中對Java堆是這樣描述的:“所有的對象實例以及數組都應當在Java堆上分配”。如果對Java堆進行細分地話,又可以分爲新生代(包含Eden空間、From Survivor空間、ToSurvivor空間)和老年代。

在這裏插入圖片描述

由來

不喜歡看歷史的可以先看後面內容。之所以將Java堆又細分爲新生代和老年代的原因不是因爲《Java虛擬機規範》對這一塊數據區域有進一步地劃分。

而是Sun/Oracle JDK和Open JDK中默認使用的HotSpot虛擬機在G1垃圾收集器出現以前,所有使用的垃圾收集器全部都是基於“分代收集理論”來設計的。

也就是說把Java堆固定劃分爲兩個區域,針對各自區域中存儲數據的特點,一種垃圾收集器工作在新生代,另一種垃圾收集器工作在老年代,二者配合共同完成垃圾收集工作。所以對Java堆的進一步細分完全是爲了方便進行垃圾收集。

但是到了今天,再這麼說就值得商量了。用《深入理解Java虛擬機:JVM高級特性與最佳實踐(第三版)》作者的原話講就是:

但是到了今天,垃圾收集器技術與十年前已不可同日而語,HotSpot裏面也出現了不採用分代設計的新垃圾收集器,在按照上面的提法就有很多需要商榷的地方了。

我理解的意思就是說,面試官想跟你聊新生代、老年代,你就跟他聊,反正咱也不是不懂,不聊就算了,但是感覺多半會聊啊,還是掌握的好。。。

三種空間的領地

既然把新生代又劃分爲Eden空間、From Survivor空間、ToSurvivor空間,那麼肯定是有比例的,誰佔多大的地,以免發生領地糾紛。

在HotSpot虛擬機中默認Eden和From Survivor、ToSurvivor的大小比例是8:1:1。所以上圖爲了展示清楚,畫的不是很準確。

分配

我們把整個Java堆比喻成一個大房子,虛擬機比喻成房子的主人小V,來看看內存分配與垃圾回收是怎麼一回事。

而且這些空間的名字太長,我們約定Eden空間——伊甸空間,From Survivor空間——from空間,To Survivor空間——to空間。

1.Eden空間+From Survivor空間

小V今天心情非常好買了好多東西(new了很多對象),回到家後他把東西都放在了這個房間,他對房間的使用就是這麼安排的,“你管我!哼!”。

即:每次分配內存只使用伊甸和from空間

2.ToSurvivor空間

有一天伊甸+from房間裝滿了。雖然小V住的是豪宅,但也經不住他是個土豪,買的東西太多了,房間再怎麼大,總有裝滿的一天。但是他又想買東西了,這個時候就只能把一些對他不重要的東西丟掉(GC),好騰出空間放新東西。

小V雖然是土豪但是一點都不蠢,他收拾伊甸+from房間的方法是這樣的。把一些對他還有用的東西放到to這個房間存起來,之後把伊甸+from房間的東西全部丟掉,心裏想着“這都是哪一年的衣服了!咱可是時尚圈的弄潮兒”,注意這句話其實是暗示哦,暗示這個對象實例已經達到了可回收的標準(也就是可達性分析算法)。

即:發生垃圾收集時,將伊甸和from中仍然存活的對象一次性複製到to空間上,然後直接清理掉伊甸和from空間。

3.老年代

(1)牀大放不下

收拾房間可是一件髒累活而且不總是那麼順利,這不,小V就遇到了麻煩。他發現伊甸+from這個房間有一張他買的雙人大牀,他想着“這個牀還是有用的哈,雖然不一定馬上用得着,但女盆朋來了,折騰起來好像更方便(伏筆)”。

那就放到to空間嘛,但是好像放不下,畢竟兩者之間比例是9:1,放不下也正常。

那怎麼辦呢?房間雖小,豪宅可不小,那就放到別的房間,搬到老年代房間去。(當然爲了防止這個牀大到連老年代都放不下,會在虛擬機會在對新生代GC之前進行判斷的,這裏不詳說,詳情可參考)

剛纔的伏筆決對是正經的,暗示這個對象實例還有用哈,並沒有達到回收標準。

即:當to空間不足以容納一次GC之後存活的對象時,就需要依賴老年代進行分配擔保。

(2)大象放哪裏?

這一次小V買了一頭大象,這真是實名副其實的大對象。這頭大象對小V來說決對是一個讓他頭疼的事,主要有下面兩個理由。

一、即便伊甸+from房間還有很多空間,但是這頭大象一來,他很大可能就要開始收拾房間了;二、後期還有可能要把這頭大象搬到to房間或老年代去,簡直欲哭無淚啊!!!

那麼幹脆直接放到老年代啊。

即:HotSpot虛擬機提供了-XX:PretenureSizeThreshold參數,指定大於該設置值的對象直接在老年代分配,這樣做的目的就是避免在伊甸區及from、to區之間來回複製,產生大量的內存複製操作。

(3)經典款

前面已經講過發生垃圾收集時,將伊甸和from中仍然存活的對象一次性複製到to空間上,然後直接清理掉伊甸和from空間。

在這之後小V又收拾了幾次房間,每次都能在to房間看見同一件衣服,小V心想着“都過去這麼久了,這件衣服還是那麼好看,不愧是經典款,那麼我就把你放到老年代房間裏去吧。”

即:對象通常在Eden區裏誕生,如果經歷第一次GC後仍然存活,並且能被to空間容納的話(容納不了就是“牀大放不下”的情況了),該對象會被移動到to空間中,並且將其對象年齡設爲1歲。

對象在to區每熬過一次GC,年齡就增加一歲,當它的年齡增加到一定程度(默認15),就會晉升到老年代中。

(4)小V就是這麼決定的

在往to房間堆放好東西之後,小V發現這一次搬進來的東西就佔了整個房間一半的地方,小V決定把這些東西移到老年代房間去。

即:如果在to空間中相同年齡所有對象大小的總和大於to空間的一半,年齡大於或等於該年齡的對象就可以直接進入老年代,無須等到-XX:MaxTenuringThreshold中要求的年齡。

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