JVM--Gc篇

Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分爲若干不同數據區域

一塊是非堆區,一塊是堆區。堆區分爲兩大塊,一個是Old區,一個是Young區。Young區分爲兩大塊,一個是Survivor區(S0+S1),一塊是Eden區。Eden:S0:S1=8:1:1 S0和S1一樣大,也可以叫From和To。
在這裏插入圖片描述

Young區:複製算法(對象在被分配之後,可能生命週期比較短,Young區複製效率比較高)
Old區:標記清除或標記整理(Old區對象存活時間比較長,複製來複制去沒必要,不如做個標記再清理)

對象創建所在區域
大家都知道對象是存在堆中的 ,上篇文章中有提到,
一般情況下,新創建的對象都會被分配到Eden區,一些特殊的大的對象會直接分配到Old區。

比如有對象A,B,C等創建在Eden區,但是Eden區的內存空間肯定有限,比如有100M,假如已經使用了100M或者達到一個設定的臨界值,這時候就需要對Eden內存空間進行清理,即垃圾收集(Garbage Collect),這樣的GC我們稱之爲Minor GC,Minor GC指得是Young區的GC。經過GC之後,有些對象就會被清理掉,有些對象可能還存活着,對於存活着的對象需要將其複製到Survivor區,然後再清空Eden區中的這些對象。

由圖解可以看出,Survivor區分爲兩塊S0和S1,也可以叫做From和To。在同一個時間點上,S0和S1只能有一個區有數據,另外一個是空的。

接着上面的GC來說,比如一開始只有Eden區和From中有對象,To中是空的。此時進行一次GC操作,From區中對象的年齡就會+1,我們知道Eden區中所有存活的對象會被複制到To區,From區中還能存活的對象會有兩個去處。若對象年齡達到之前設置好的年齡閾值,此時對象會被移動到Old區,沒有達到閾值的對象會被複制到To區。此時Eden區和From區已經被清空(被GC的對象肯定沒了,沒有被GC的對象都有了各自的去處)。這時候From和To交換角色,之前的From變成了To,之前的To變成了From。也就是說無論如何都要保證名爲To的Survivor區域是空的。Minor GC會一直重複這樣的過程,知道To區被填滿,然後會將所有對象複製到老年代中。

從上面的分析可以看出,一般Old區都是年齡比較大的對象,或者相對超過了某個閾值的對象。在Old區也會有GC的操作,Old區的GC我們稱作爲Major GC,每次GC之後還能存活的對象年齡也會+1,如果年齡超過了某個閾值,就會被回收。

對象的一輩子理解

我是一個普通的Java對象,我出生在Eden區,在Eden區我還看到和我長的很像的小兄弟,我們在Eden區中玩了挺長時間。有一天Eden區中的人實在是太多了,我就被迫去了Survivor區的“From”區,自從去了Survivor 區,我就開始漂了,有時候在Survivor的“From”區,有時候在Survivor的“To”區,居無定所。直到我18歲的時候,爸爸說我成人了,該去社會上闖闖了。於是我就去了年老代那邊,年老代裏,人很多,並且年齡都挺大的,我在這裏也認識了很多人。在年老代裏,我生活了20年(每次GC加一歲),然後被回收。

爲什麼需要Survivor區?只有Eden不行嗎?

如果沒有Survivor,Eden區每進行一次MinorGC,存活的對象就會被送到老年代。這樣一來,老年代很快被填滿,觸發MajorGC(因爲MajorGC一般伴隨着MinorGC,也可以看做觸發了FullGC)。老年代的內存空間遠大於新生代,進行一次FullGC消耗的時間比MinorGC長得多。執行時間長有什麼壞處?頻發的FullGC消耗的時間很長,會影響大型程序的執行和響應速度。

所以Survivor的存在意義,就是減少被送到老年代的對象,進而減少FullGC的發生,Survivor的預篩選保證,只有經歷16次MinorGC還能在新生代中存活的對象,纔會被送到老年代。

爲什麼需要兩個Survivor區?

最大的好處就是解決了碎片化。也就是說爲什麼一個Survivor區不行?第一部分中,我們知道了必須設置Survivor區。假設現在只有一個Survivor區,我們來模擬一下流程:剛剛新建的對象在Eden中,一旦Eden滿了,觸發一次MinorGC,Eden中的存活對象就會被移動到Survivor 區。這樣繼續循環下去,下一次Eden滿了的時候,問題來了,此時進行MinorGC,Eden和Survivor各有一些存活對象,如果此時把Eden區的存活對象硬放到Survivor區,很明顯這兩部分對象所佔有的內存是不連續的,也就導致了內存碎片化。永遠有一個Survivorspace是空的,另一個非空的Survivorspace無碎片。

Garbage Collect(垃圾回收)

如何確定一個對象是垃圾?

引用計數法
對於某個對象而言,只要應用程序中持有該對象的引用,就說明該對象不是垃圾,如果一個對象沒有任何指針對其引用,它就是垃圾。弊端:如果AB相互持有引用,導致永遠不能被回收。

可達性分析
通過GC Root的對象,開始向下尋找,看某個對象是否可達

垃圾收集算法

已經能夠確定一個對象爲垃圾之後,接下來要考慮的就是回收,怎麼回收呢?得要有對應的算法,下面介紹常見的垃圾回收算法。

標記-清除(Mark-Sweep)

找出內存中需要回收的對象,並且把它們標記出來 比較耗時,需要把堆中所有的對象都掃描一遍,才能確定那個是垃圾, 然後清除掉標記的對象,從而釋放內存空間

複製(Copying)

將內存劃分爲兩塊相等的區域,每次只使用其中一塊
當其中一塊內存使用完了,就將還存活的對象複製到另外一塊上面,然後把已經使用過的內存空間一次清除掉。

標記-整理(Mark-Compact)

標記過程仍然與"標記-清除"算法一樣,但是後續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的內存。

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