JAVA 垃圾回收機制系列文章算法
系列文章分3個部分安全
從 JAVA 垃圾回收機制(一) --- 對象回收與算法初識 中,咱們已經知道了 GC 回收時的一些原理和算法總結。這一章,咱們來看看 虛擬機中究竟是怎麼實現這些步驟的。spa
從上篇知道了 可達性分析中,有個叫 GC Roots節點的名詞;那在進行 GC 時,就須要對全部對象找到這個 GC Roots 的節點,而在找的同時,是不能出現對象引用關係在不斷變化的。這點是致使GC進行時必須停頓全部的Java執行線程的其中一個重要緣由。 那既然沒法避免這種狀況,就須要如何去優化這個時間了,畢竟你忽然寫到一半,來了個停頓5分鐘,誰都會崩潰的。.net
由於並不須要一個不漏的檢查全部的執行上下文和引用位置,虛擬機中,能夠一組被稱爲 OopMap 的數據結構來達到目的。在類加載完的時候,HotSpot (虛擬機的一種)就已經把對象內的偏移量上是什麼類型的數據計算出來了,即位置也會被 OopMap 記錄下來。線程
有了OopMap 的幫忙下,能夠快速且準備的找到 GC Roots 枚舉,但可能因爲引用關係的變化,或者說 OopMap 內容變化的指令很是多,若是爲每一條指令都生成對應的 OopMap ,那將會須要大量的額外控件,這樣GC成本也會變高。 因此,系統並無爲每條指令都生成OopMap,而是在「特定的位置」記錄了這些信息,好比方法的調用、循環跳轉、異常跳轉等,這些位置被稱爲安全點,只有在達到安全點纔會去停頓。跑到安全點的防範有兩種:code
上面貌似解決了問題,當若是線程處於 sleep 或者 Blocked 狀態時,此時系統又發出了GC,這時安全點就沒啥用了;因此,這裏又設置了一個安全區域;安全區域是指在一段代碼中,引用關係不會發現變化。在這個區域 GC 都是安全的,當在這個區域發現GC,線程能夠無論本身的安全點的狀態,當要離開這段區域時,須要檢查是否完成根節點枚舉,沒有則等待,只有收到能夠離開安全點的信號爲止。對象
內存分配在上一章也講了很多;從大方向來說,對象主要分配在 堆 上,對象主要分配在新生代的 Eden 分區上,若是啓動了本地線程緩衝,將按線程有線分配在CLAB上;少數狀況下也會分配在老分區上。 首先先要理解,新生代和老年代都是一個內存空間,由參數配置,只是能夠根據算法,決定對象是在新生代仍是在老年代的內存區域!!!
舉個簡單例子:假如虛擬機中設置了新生代的內存大小爲10M,老年代的也爲10M,Eden 和 Survivor 爲 8:1 的關係,那麼Eden就只有8M,Survivor 爲1M;下面建立4個對象
public static void test(){
// 假設 _1MB 字符創爲 1MB
byte[] b1 = new byte[2*_1MB];
byte[] b2 = new byte[2*_1MB];
byte[] b3 = new byte[2*_1MB];
byte[] b4 = new byte[4*_1MB];
}
複製代碼
當執行 test() ,當要分配 b4 對象時,會執行一次 Minor GC,緣由是 Dden 才 6M,被b1,b2,b3填充以後,已經沒有數據去填充 b4了,就會觸發 GC,而b4沒辦法,只有移動到老年代的內存區域了。
從上面能夠看,假如一個 2MB 的數據,這個短命的大對象在新生去上朝生夕死,很容易觸發 GC ,因此能夠經過設置PretenureSize 設置 閾值,對象大於這個參數,直接進去老年代
一塊內存能夠分爲3個區域,一個 Eden 和兩個 Survivor 區,當對象在 Eden 建立,並經理了第一次 GC 以後仍然存活,而且能被 survivor 區容納的話,將移到 survivor 區;對象在Survivor 區中「熬過」一次,年齡增長1,當增長到 15 歲(默認,這個閾值能夠經過 -XX:MaxTenuringThreshold 設置),就會晉升成老年代的對象。 從這裏來看,能夠獲得兩個結論
上面說到默認年齡爲 15 才進入老年代,其實並否則,只要Survivor 中的對象總和大於Survivor 空間的一半,年齡大於或等於該年齡的對象就能夠直接進入老年代。以下:
public static void test(){
// 假設 _1MB 字符創爲 1MB
byte[] b1 = new byte[_1MB/4];
byte[] b2 = new byte[_1MB/4];
// b1 + b2 的總和大於 Survivor 的一半
byte[] b3 = new byte[4*_1MB];
byte[] b4 = new byte[4*_1MB];
}
複製代碼
當運行 test() ,發現 Survivor 依舊爲0,b1和b2進入進入老年代,由於它們是同齡的。
在發生GC前,虛擬機會先檢查老年代的可用連續空間是否大於新生代的全部對象,若是成立,則發生 GC 是安全的。若是不成立,則會看是否容許擔保失敗,若是不容許,則進行 Full GC;若是容許,則會繼續檢查老年代的可用連續空間是否大於歷次晉升到老年代對象的平均大小,若是大於,則進行 Minor GC,若是小於,則進行 Full GC ,但若是某次 Minor GC 後存活的對象大於平均值,會致使擔保失敗,失敗以後,也會進行Full GC。
此致,Java 的GC 機制就分析完啦