gc原理以及gc日誌剖析

gc原理以及gc日誌

一.概述

學習Java的我們都知道垃圾收集(gc),大部分人把這項技術當作是java語言的伴生產物。事實上,gc的歷史比Java久遠,1960年誕生於MIT的Lisp是第一門真正使用內存動態分配和垃圾收集技術的語言。那我們今天就研究下垃圾收集原理。

二.對象已死嗎?

Java的垃圾回收主要是對推內存的回收,裏面存放着Java幾乎所有的對象實例,垃圾回收之前是要確定哪些還“存活”哪些已經“死去”。

1.引用計數器法

給對象添加一個引用計數器,每當有地方對他進行引用時計數器值➕1;當引用失效時,計數器值就➖1,任何時候計數器值爲0的時候表示對象不可能在使用的。

2.可達性分析算法

通過一系列稱爲“GC Roots”的對象作爲起點,從這些節點往下搜索,搜索所走過的路徑稱爲“引用鏈”,當一個對象到“GC Roots”沒有任何引用鏈相連時,則證明對象不可用的

代碼示例:

gc日誌如下:

我們很明顯的看到gc日誌中6092K->456K,意味着虛擬機並沒有因爲這兩個對象互相引用而不回收他們,所以java虛擬機使用的是可達性分析算法標記的。

其實即使被可達性分析算法標記的不可達對象也不是一定會被回收的,虛擬機會對這些對象進行一次篩選,篩選的條件是此對象是否有必要執行finalize()方法。當對象沒有覆蓋finalize()方法或者finalize()已經被虛擬機調用過,虛擬機將這兩種情況視爲“沒有必要執行”。如果對象被判定有必要執行finalize()方法是並在finalize()中與“GC Roots”建立關聯則次對象不會被回收了。

三.垃圾回收算法

我們知道了虛擬機怎麼標記一個對象是否可用,那他怎麼進行回收的呢?其實堆內存可以分爲新生代和老年代,新生代又被劃分爲一個Eden和兩個Survivor區域他們的比例爲8:1:1,不同的垃圾收集器廠商給對這兩個區域給出了不同的算法。

1.新生代——複製算法

新生代對象的特點就是,大部門對象在一次GC中會被回收掉,所以使用的是複製算法:新生代每次創建對象的時候只會使用一個Eden和其中的一塊Survivor,在垃圾回收時將存活的對象複製到另外一塊Survivor區域,最後清理掉Eden和剛纔的Survivor區域。

1.老年代——標記-整理算法

老年代一般保存的是一些大對象,或者不被經常回收的對象,根據特點使用的標記-整理算法:如同名字一樣,算法分爲"標記"和“整理”兩個階段:首先先標記出所有需要回收的對象,在標記完成後統一回收所以被標記的對象進行整理,將被標記的對象都向一端移動,然後直接清理掉邊界以外的內存。

四.HotSpot算法實現

上面說了我們怎麼標記對象“死亡”和怎麼進行垃圾回收的,但在HotSpot虛擬機在實現這些算法上是必須對算法的執行效率進行考量的。

1.安全點

在可達性分析中對執行的時間的敏感體現在GC停頓上,其意思是在整個分析的過程中看起來就像被凍結在某一個時間點上的,不可以出現分析的過程中引用關係在不斷變化,如果這點得不到保證則分析的結果的準確性就得不到保障。這點是導致在GC進行時需要停頓所有的java執行線程。當執行系統停頓下來後,虛擬機並不需要全部上小文和全局所有的位置,虛擬機通過一個OopMap的數據結構在類加載的時候將對象的偏移量數據信息記錄下來,所以GC掃描是就直接得到這些信息的。其實這些通過指令被加入進行記載對象信息的OopMap位置也叫做安全點,程序執行時並非所有點都可以停下來開始GC的,只有在到達安全點才能停頓。安全點機制程序執行中,在不太長的時間內會遇到可進入GC的安全點。在實際中會遇到在GC時有線程不再執行,例如線程被掛起了。這是我們需要安全區域去解決。

2.安全區域

安全區域是指在一段代碼片段中,引用關係不會發生變化。在這個區域中的任意地方開始GC都是安全的。在代碼執行到安全區域是,首先表示這直接進去安全區域,這樣虛擬機在這段時間GC時就不用管那些標記爲安全區域的線程了。當離開安全區域時首先得判斷是GC分析是否完成,沒完成則需要等待。

五.理解GC日誌

這是上圖打印的gc日誌

[GC (System.gc()) [PSYoungGen: 6092K->448K(38400K)] 6092K->456K(125952K), 0.0051702 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]

PSYoungGen表示的是新生代gc不同垃圾收集器新生代名稱不一樣,6092K->448K(38400K)表示新生代大小的變化,6092K->456K(125952K)表示推內存的大小變化 後面表示用時。

[Full GC (System.gc()) [PSYoungGen: 448K->0K(38400K)] [ParOldGen: 8K->378K(87552K)] 456K->378K(125952K), [Metaspace: 3050K->3050K(1056768K)], 0.0056045 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

表示發生在老年代的gc(Major GC/Full GC) 他只是爲伴隨一次的新生代的gc(Minor GC),448K->0K(38400K)表示新生代內存變化,8K->378K(87552K) 老年代gc變化,456K->378K(125952K)表示gc前後堆內存的變化。

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