之前我們學習了 JVM 基本介紹 以及 什麼樣的對象需要被 GC ,今天就來學習一下 JVM 在判斷出一個對象需要被 GC 會採用何種方式進行 GC。在學習 JVM 如何進行垃圾回收方法時,發現所謂的 JVM 垃圾回收思想和現實生活的場景有很多相似的地方。所以本文用餐廳回收餐桌的方式類比 JVM 垃圾回收算法,應該能幫助 JVM 學習的理解和記憶。
經典垃圾回收算
標記-清除(Mark-Sweep)
研發園開了家新餐廳,餐廳老闆在考慮如何回收餐盤時首先使用了最簡單的方式,那就是服務員在顧客用餐的過程中,不定時的觀察餐廳,針對用完餐的顧客記錄他們的位置(當然一般的服務員的腦海中自行處理),統一回收他們的餐具和餐盤。但是這種回收方式會有一個明顯的問題,那就是回收後的餐廳座位,很有可能是不連續的。如果後續有同行的顧客(比如小情侶... ...)想坐在一起,那很可能找不到連續的座位。
複製算法(Copying)
爲了解決餐廳座位碎片化的問題,餐廳的老闆提出了一個大膽的想法,這是一個很會思考的老闆。把餐廳的用餐區域分成兩部分 A 廳和 B 廳,當對 A 廳中的餐桌做回收時,將 A 廳中還未用完餐的顧客,"請"到 B 廳去用餐,並且讓這些顧客在 B 廳中拼桌用餐(爲了餐位連續)。這樣所有 A 廳中的位置都空餘出來了,並且 B 廳中的用餐區域和未用餐區域都是連續的!簡直是強迫症晚期。看似完美的解決了回收後餐位碎片化的問題。但是依然帶來了其他的一些問題。
缺點:
-
餐廳的運營區域是一個整體,現在只能同時對外開放 A 廳,運營空間變小了;
-
當有很多顧客需要從 A 廳轉移到 B 廳時,效率太低;
-
用餐體驗很差,不被罵娘纔怪。
優點:
-
不容易產生碎片
標記-整理算法(Mark-Compact)
當實行複製算法解決餐位回收後不連續的問題,餐廳的老闆針對新問題又有了新想法,只要移動顧客就可以解決碎片化問題,爲啥我要將餐廳分成兩個的部分呢?畢竟那樣不能最大效率的利用餐廳的用餐區域。創造性的提出了標記-整理算法,結合前面兩中方法的優缺點,當餐廳準備回收餐位時,移動所有未用晚餐的顧客,並且讓從餐廳的第一桌開始拼桌。這樣保證後面的餐桌都是已經回收了餐盤的並且回收後的座位都是連續的。這樣既提高了餐廳餐桌的利用率又保證了當有大量組團顧客進店用餐時,餐廳能夠提供大量的連續餐桌(這個例子不恰當,如果真的碰到一個要讓人不斷移動形成空位的餐廳,你怕是要開噴了)。
分代收集(Generational Collection)
如果還是用開餐廳的方式來思考 JVM 的話,可以把分代回收看做餐廳針對不同顧客的等級推出的個性化服務。分代收集算法並沒有新的思想,只是根據對象存活週期的不同將內存劃分爲幾塊,一般把 Java 堆分爲新生代和老年代,這樣就可以根據各個年代的特點採用最適當的收集算法。在新生代中,大量的對象都是“朝生夕死”,每次垃圾回收時,都可以發現大量對象死去,所以針對新生代的垃圾回收一般選擇複製算法。只需要複製少量存活對象就可以完成收集。針對老年代的垃圾回收,對象的存活時間較長,就必須使用標記-清除或者標記-整理算法來進行回收。
在新生代中,絕大多數的對象都是“朝生夕死”的,新生代並不需要按照 1:1 的比例劃分內存空間,而是將內存分爲一個較大的 Eden 空間和一個較小的 Survivor 空間,並將 Survivor 空間分成兩個較小空間,分別是 From Space 和 ToSpace。每次使用 Eden 空間和其中的一塊 Survivor 空間,當進行回收時,將該兩塊空間中還存活的對象複製到另一塊 Survivor 空間中。Hotspot 虛擬機默認 Eden 和 Survivor 的大小比例是 8:1,也就是每次新生代可用內存空間爲整個新生代容量的 90%。
這裏也可以簡單理解爲我們給餐廳的忠實吃貨辦了張 VIP 卡... ...
所以,同學,辦卡嗎?辦卡享五折優惠哈!
... ...
參考文檔