JVM GC算法

GC Roots:

 The objects that a program can access directly are those objects which are referenced by local variables on the processor stack as well as by any static variables that refer to objects. In the context of garbage collection, these variables are called the roots

在Java語言中,包括但不限於以下幾種:

1.虛擬機棧中引用的對象

2.方法區中類靜態屬性引用的對象

3.方法區中常量引用的對象

4.本地方法棧中JNI引用的對象

 

哪些對象該被GC?

如果一個對象到GC Roots沒有任何引用鏈,該對象就是不可達對象,即可以被GC。

 

句柄(Handles):

The Java virtual machine specification does not prescribe how reference variables are implemented. A common approach is for a reference variable to be implemented as an index into an array of object handles . Every object instance has its own handle. The handle for an object typically contains a reference to a Class instance that describes the type of the object and a pointer to the region in the heap where the object data resides.

也就是說我們代碼中的引用可能是直接引用對象,也可能是引用的是對象的句柄,對象的句柄再引用對象。用句柄的好處是當對象的地址改變時,只需改變句柄指向新的地址即可,即只需改一個地方,但是如果不用句柄的話就需要更新每一個引用。下載

 

 

一、標記清除算法(Mark-and-Sweep)

 When using mark-and-sweep, unreferenced objects are not reclaimed immediately. Instead, garbage is allowed to accumulate until all available memory has been exhausted. When that happens, the execution of the program is suspended temporarily while the mark-and-sweep algorithm collects all the garbage. Once all unreferenced objects have been reclaimed, the normal execution of the program can resume.

 

標記清除算法是最基礎的收集算法,後面的算法都是基於該算法擴展的,分爲兩個階段:

標記階段(Mark phase):

從GC Roots出發,沿着引用鏈,遇到對象就標記該對象,直到所有可達對象都被標記。

清除階段(Sweep phase):

掃描整個heap,回收那些在標記階段未被標記的對象的內存。

 

標記(mark)僞碼描述:下載

Java代碼 

  1. for each root variable r  

  2.     mark (r);  

  3. sweep ();  

 

Cpp代碼 

  1. void mark (Object p)  

  2.     if (!p.marked)  

  3.         p.marked = true;  

  4.         for each Object q referenced by p  

  5.             mark (q);  

 清除(sweep)僞碼描述:下載

Cpp代碼 

  1. void sweep ()  

  2.     for each Object p in the heap  

  3.         if (p.marked)  

  4.             p.marked = false  

  5.         else  

  6.             heap.release (p);  

 

圖示描述(只有一個root,a爲標記前,b爲標記後清除前,c爲清除後):

 

 

標記清除算法缺點:1.GC時用戶線程必須暫停;2.標記和清除效率都不高;3.標記清除後會產生大量的內存碎片

 

 

二、停止複製算法(Stop-and-Copy)

When using the stop-and-copy garbage collection algorithm, the heap is divided into two separate regions. At any point in time, all dynamically allocated object instances reside in only one of the two regions--the active region. The other, inactive region is unoccupied.

 

停止複製算法將可用內存分爲等量的兩塊,每次只使用其中的一塊,當這一塊無法滿足新的內存分配時,即需要GC時,就掃描該塊,將還存活的對象複製到另一塊上面,然後再把已使用過的內存空間一次清理掉。

 

據研究,新生代的對象大多是短命的對象,所以不需要按照1:1的比例來劃分內存空間,而是將內存劃分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor。當回收時,將Eden和Survivor中還存活的對象一次複製到另外一塊Survivor上,最後再清理Eden和原Survivor。Hotspot默認的比例Eden :Survivor = 8 :1.可以通過 -XX:SurvivorRatio=<N>來指定。值得注意的是,若複製時Survivor不夠空間存放存活的Object了,則剩餘的Object將進入Tenured Generation,即老年代。

 

Copy算法僞碼描述(activeHeap:當前存放對象的那一塊,inactiveHeap:當前未存放對象的那一塊,forward引用所屬對象在另一塊中的複製對象,forward爲null說明該對象還未被複制到另一塊):下載

Cpp代碼 

  1. for each root variable r  

  2.     r = copy (r, inactiveHeap);  

  3. swap (activeHeap, inactiveHeap);  

 

Cpp代碼 

  1. void Object copy (Object p, Heap destination)  

  2.     if (p == null)  

  3.         return null;  

  4.     if (p.forward == null)  

  5.         q = destination.newInstance (p.class);  

  6.         p.forward = q;  

  7.         for each field f in p  

  8.             if (f is a primitive type)  

  9.                 q.f = p.f;  

  10.             else  

  11.                 q.f = copy (p.f, destination);  

  12.         q.forward = null;  

  13.     return p.forward;  

 

圖示描述:

 

 

 

停止複製算法缺點:1.每次GC都要複製所有存活的對象,如果對象的存活率很高,那代價未免太大了;2.內存浪費,即使是按照Hotspot的Eden-Survivor來劃分,仍有不少內存是浪費的。

 

 

三、標記整理(壓縮)算法(Mark-and-Compact)

 

標記整理算法和標記清理算法類似,也分爲兩個階段:

標記階段(Mark):從GC Roots出發,沿着引用鏈,遇到對象就標記該對象,直到所有可達對象都被標記。

整理(壓縮)階段(Compact):把所有的存活對象往一端移,然後清理掉端邊界外的內存。

 

圖示句柄的用法:

 

 

可以看到所有對象都是和句柄關聯而不是直接和引用(變量)關聯。

 

標記過程僞碼描述:下載

Cpp代碼 

  1. void mark (Object p)  

  2.     if (!handle[p].marked)  

  3.         handle[p].marked = true;  

  4.         for each Object q referenced by p  

  5.             mark (q);  

 整理(壓縮)算法僞碼描述:

Cpp代碼 

  1. void compact ()  

  2.     long offset = 0;  

  3.     for each Object p in the heap  

  4.         if (handle[p].marked)  

  5.             handle[p].object = heap.move (p, offset);  

  6.             handle[p].marked = false;  

  7.             offset += sizeof (p);  

 

 

 

 注意:無論是哪種算法哪種收集器,在枚舉根節點時都是要停頓(停頓指得是暫停所有用戶線程),只是根據算法的不同以及收集器的不同該停頓時間的長短不同而已。


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