爲什麼HotspotJVM垃圾回收中的“標記-複製”算法需要兩個survivor空間?

一、提出問題

如題,爲什麼HotspotJVM垃圾回收中的“標記-複製”算法需要兩個survivor空間?爲什麼要強調Hotspot JVM呢,因爲JVM有很多種,每種JVM的實現方式都不一樣。本文提到的JVM,一律是代表Hotspot JVM。

二、背景

熟悉jvm的童鞋,都應該瞭解到,一些經典的新生代垃圾收集器運用了“標記-複製算法”,並且,爲了較好的實現複製算法,通常把新生代分爲兩種邏輯分區,一種叫eden空間,另一種叫survivor空間。jvm給一個對象分配內存時,會優先分配到eden空間;然後survivor,顧名思義就是從eden空間經歷過Minor GC但仍“得以生存”的那些對象存儲的內存空間。
那按照jvm實現的思路,爲什麼需要兩個survivor空間呢?
這個問題用反證法比較好理解,我們作以下幾個假設。
在這裏插入圖片描述

三、假設

這裏設置新生代被分配的內存空間是10,作以下假設:

  1. 假設沒有survivor空間,eden空間和survivor空間大小比是10:0。 這個假設有點不攻自破,因爲既然是複製算法,肯定是至少有一個原來的內存空間,以及一個要複製到的目標空間,所以內存只有一個邏輯分區(這裏指eden空間)是不夠的,至少要兩個邏輯分區,才能實現“得救者”的轉移。
  2. 假設只有一個survivor,eden空間和survivor空間大小比是5:5。 這樣一來,把新生代內存分成兩個空間,eden和surivor各佔一半。我們可以想象一下,jvm會在eden空間爲新對象分配內存,然後到MinorGC發生時,eden空間中得以存活的對象被複制並移動到survivor空間(假設survivor空間足夠大),然後把eden空間清空,eden空間和survivor空間角色互換,準備下一次新的對象空間的分配。這個過程看起來毫無破綻,但細心點看,可以發現,每次可分配的內存空間大小隻有新生代空間的一半,這未免也太浪費了吧?
    根據IBM一項專門的研究表明:新生代的對象有98%是”朝生夕滅“,什麼概念呢?也就是說很多對象都熬不過第一次MinorGC。所以,如果eden空間和survivor空間設置成一樣大小的兩塊,那麼也許會看到這麼一種現象:Eden空間很滿,而survivor空間幾乎是空的。這樣也可能引發嚴重的問題,就是如果來了一個超大對象(比eden空間還大),由於eden空間被排擠得太小,而不得不把這個超大對象提前晉升到老年代,而造成老年代的空間瞬間膨脹,可能會引發FullGC,從而導致用戶程序卡頓。
    在這裏插入圖片描述
  3. 假設只有一個survivor,eden空間和survivor空間大小比是9:1。 再來看看survivor空間只有一個的另外一個情況,這次是把survivor縮小到是eden的1/9。因爲新生代只劃分了兩個空間,所以發生MinorGC的處理方式跟上一條是一模一樣的,但到了最後一步,因爲eden和survivor的空間大小是9:1,之後如果發生角色互換,則eden和survivor大小比是1:9,這對對象的內存分配來說,是無法接受的,所以這種情況也是不妥的。

四、結論

經過以上的幾種假設,我們可以得出結論。如果eden空間和survivor空間設置成一樣的大小,是可以實現“半區複製”的,但這種做法來劃分jvm的新生代是不合理的。正確的做法我們都知道了:eden空間佔絕大部分,再設置兩個相同大小的from survivor空間和to survivor空間,三者默認大小比是8:1:1,發生MinorGC時,eden和from survivor中被判斷存活的對象被移動到to survivor,剩下的內存空間都被回收,然後from survivor和to survivor空間互換,此時的eden和from survivor都是被清空了的,準備下一輪的對象分配和GC 而控制這個比例的參數是-XX:+SurvivorRatio,如-XX:SurvivorRatio=8代表eden空間大小佔8,另外兩個survivor空間各佔1。

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