聊聊Java的GC機制

做者 某人Valar
如需轉載請保留原文連接
部分圖片來自百度,若有侵權請聯繫刪除html

本文目錄java

  • 什麼是GC
  • JVM內存結構簡單介紹
  • 可達性分析與GC Roots
  • 常見的垃圾收集算法

1. 什麼是GC

GC:垃圾回收(Garbage Collection),在計算機領域就是指當一個計算機上的動態存儲器(內存空間)再也不須要時,就應該予以釋放,以讓出存儲器,便於他用。這種存儲器的資源管理,稱爲垃圾回收。算法

有一些語言是沒有垃圾回收機制的,像CC++,若是須要釋放無用變量內存空間就由本身來處理。bootstrap

而其餘的一些語言如JavaC#都支持垃圾回收器,Java虛擬機(JVM)或.NET CLR發現內存資源緊張的時候,就會自動地去清理無用對象(沒有被引用到的對象)所佔用的內存空間。bash

而咱們今天主要講Java中的GC。網絡


上面說到JVM會自動清理無用的對象,那麼咱們就有了疑問:oracle

  • JVM清理的是哪一塊的對象?
  • 哪些對象會被清理,爲何清理A而不清理B?
  • JVM又是如何清理的?

這三個問題將分別對應接下來的3節一一解答。eclipse

2. JVM內存結構簡單介紹

咱們都知道,Java代碼是要運行在虛擬機上的,而虛擬機在執行Java程序的過程當中會把所管理的內存劃分爲若干個不一樣的數據區域,這些區域都有各自的用途。 在《Java虛擬機規範(Java SE 8)》中描述了JVM運行時內存區域結構以下:jvm

以上是Java虛擬機規範,不一樣的虛擬機實現可能會各有不一樣,可是通常會遵照規範jsp

  • 方法區:存儲已被虛擬機加載的類信息、常量、靜態變量等
  • 堆:堆是Java 虛擬機所管理的內存中最大的一塊。惟一目的就是存放對象實例。在虛擬機棧中存放的只是引用,而引用指向的是堆中的對象。GC主要做用的區域
  • 虛擬機棧:局部變量表、操做數棧等。虛擬機棧描述的是Java 方法執行的內存模型:每一個方法被執行的時候都會同時建立一個棧幀(Stack Frame)用於存儲局部變量表、操做棧、動態連接、方法返回地址等信息。
  • 本地方法棧:與虛擬機棧相似,是爲native方法提供服務的。
  • 程序計數器:記錄當前線程執行的方法執行到了第幾行。若是線程正在執行的是一個Java 方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址。若是正在執行的是Natvie 方法,這個計數器值則爲空(Undefined)。

3.可達性分析與GC Roots

3.1 可達性分析

Java中經過可達性分析法來肯定某個對象是否是「垃圾」。

該方法的基本思想是經過一系列的「GC Roots」對象做爲起點進行搜索,若是在「GC Roots」和一個對象之間沒有可達路徑,則稱該對象是不可達的,不過要注意的是被斷定爲不可達的對象不必定就會成爲可回收對象。被斷定爲不可達的對象要成爲可回收對象必須至少經歷兩次標記過程,若是在這兩次標記過程當中仍然沒有逃脫成爲可回收對象的可能性,則基本上就真的成爲可回收對象了。

注意其本質是經過找出全部活對象來把其他空間認定爲「無用」,而不是找出全部死掉的對象並回收它們佔用的空間.

以下圖,當object五、六、7不存在到GC Roots的引用時,即不可到達GC Roots,則斷定他們是不可達的。

3.2 GC Roots

對於哪些對象能夠被當成GC Roots網上有不少種說法,有的不夠權威、有的不夠全面。 最終找到了一份eclipse的官方文檔,裏面有以下的介紹: Garbage Collection Roots (本身翻譯了一下,若有不許確的地方,請指出)

A garbage collection root is an object that is accessible from outside the heap. The following reasons make an object a GC root:

1. System Class (被boostrap 或者系統類加載器加載的系統類)
    Class loaded by bootstrap/system class loader. For example, everything from the rt.jar like java.util.* .
   
2. JNI Local( 一些用戶定義jni 代碼或者jvm的內部代碼局部變量)
    Local variable in native code, such as user defined JNI code or JVM internal code.
    
3. JNI Global( jni 代碼中的全局變量)
    Global variable in native code, such as user defined JNI code or JVM internal code.
    
5. Thread Block(被阻塞的線程引用的對象)
    Object referred to from a currently active thread block.

6. Thread (正在運行的線程)
    A started, but not stopped, thread.
    
7. Busy Monitor(正在等待的線程)
    Everything that has called wait() or notify() or that is synchronized.
    For example, by calling synchronized(Object) or by entering a synchronized method. 
    Static method means class, non-static method means object.
    
8. Java Local(仍然在線程的棧中的方法的傳入參數或方法內部建立的對象)
    Local variable.
    For example, input parameters or locally created objects of methods that are still in the stack of a thread.

9.Native Stack(本地方法棧中輸入或輸出參數,例如,用於文件/網絡I/O的方法或反射的參數。)
    In or out parameters in native code, such as user defined JNI code or JVM internal code. 
    This is often the case as many methods have native parts and the objects handled as method parameters become GC roots.
    For example, parameters used for file/network I/O methods or reflection.
    
10.Finalizable(在回收隊列中的對象)
    An object which is in a queue awaiting its finalizer to be run.
    
11. Unfinalized(覆蓋了finalize方法可是尚未被放入回收隊列中的對象)
    An object which has a finalize method, but has not been finalized and is not yet on the finalizer queue.

12.Unreachable(一個從任何其餘根沒法訪問的對象,但由Memory Analyzer Tool 標記爲根,以便該對象能夠包含在分析中)
    An object which is unreachable from any other root, 
    but has been marked as a root by MAT to retain objects which otherwise would not be included in the analysis.

13. Java Stack Frame
    A Java stack frame, holding local variables.
    Only generated when the dump is parsed with the preference set to treat Java stack frames as objects.
    
14. Unknown
  An object of unknown root type. Some dumps, such as IBM Portable Heap Dump files, do not have root information. 
  For these dumps the MAT parser marks objects 
  which are have no inbound references or are unreachable from any other root as roots of this type. 
  This ensures that MAT retains all the objects in the dump.
複製代碼

簡單總結下就是:

  • 由系統類加載器(system class loader)加載的對象
  • 活着的線程,包含處於等待或阻塞的線程
  • 當前被調用的方法(Java方法、native方法)的一些參數/局部變量
  • 方法區中靜態變量、常量引用的對象
  • Held by JVM - JVM因爲特殊目的爲GC保留的對象,但實際上這個與JVM的實現是有關的。可能已知的一些類型是:系統類加載器、一些JVM知道的重要的異常類、一些用於處理異常的預分配對象以及一些自定義的類加載器等。

4. 常見的垃圾回收算法

4.1 Mark-Sweep(標記-清除)算法

這是最基礎的垃圾回收算法,之因此說它是最基礎的是由於它最容易實現,思想也是最簡單的。標記-清除算法分爲兩個階段:標記階段和清除階段。標記階段的任務是標記出全部須要被回收的對象,清除階段就是回收被標記的對象所佔用的空間。具體過程以下圖所示:

優缺點:

  • 從圖中能夠很容易看出標記-清除算法實現起來比較容易
  • 可是有一個比較嚴重的問題就是容易產生內存碎片,碎片太多可能會致使後續過程當中須要爲大對象分配空間時沒法找到足夠的空間而提早觸發新的一次垃圾收集動做。
4.2 Copying(複製)算法

爲了解決Mark-Sweep算法的缺陷,Copying算法就被提了出來。它將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另一塊上面,而後再把已使用的內存空間一次清理掉,這樣一來就不容易出現內存碎片的問題。具體過程以下圖所示:

優缺點:

  • 這種算法實現簡單,運行高效且不容易產生內存碎片
  • 可是卻對內存空間的使用作出了高昂的代價,由於可以使用的內存縮減到原來的一半。
4.3 Mark-Compact(標記-整理)算法

該算法標記階段和Mark-Sweep同樣,可是在完成標記以後,它不是直接清理可回收對象,而是將存活對象都向一端移動,而後清理掉端邊界之外的內存。具體過程以下圖所示:

優缺點:

  • 標記-整理算法是在標記-清除算法的基礎上,又進行了對象的移動,所以成本更高
  • 可是卻解決了內存碎片的問題。
4.4 Generational Collection(分代收集)算法

分代收集算法是目前大部分JVM的垃圾收集器採用的算法。它的核心思想是根據對象存活的生命週期將內存劃分爲若干個不一樣的區域。 通常狀況下將堆區劃分爲老年代(Tenured Generation)和新生代(Young Generation),在HotSpot中,設計者將方法區歸入也歸入了GC分代收集,併爲其起了一個名字,永久代(PerGen space)

  • 老年代:特色是每次垃圾收集時只有少許對象須要被回收,通常使用的是標記-整理(Mark-Compact)標記-清除(Mark-Sweep)算法。
  • 新生代:特色是每次垃圾回收時都有大量的對象須要被回收,對於新生代都採取複製(Copying)算法。

由於新生代中每次垃圾回收都要回收大部分對象,也就是說須要複製的操做次數較少,可是實際中並非按照1:1的比例來劃分新生代的空間的,通常來講是將新生代劃分爲一塊較大的Eden空間(伊甸園,亞當和夏娃偷吃禁果生娃娃的地方,用來表示內存首次分配的區域,再貼切不過)和兩塊較小的Survivor空間,每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將Eden和Survivor中還存活的對象複製到另外一塊Survivor空間中,而後清理掉Eden和剛纔使用過的Survivor空間。通常來講分配比例爲eden 80%、survivor1 10%、survivor2 10%。

  • 永久代: 方法區和永久代的關係很像Java中接口和類的關係,類實現了接口,而永久代就是HotSpot虛擬機對虛擬機規範中方法區的一種實現方式。

在方法區進行垃圾回收通常」性價比」較低, 由於在方法區主要回收兩部份內容: 廢棄常量和無用的類。 回收廢棄常量與回收其餘年代中的對象相似, 但要判斷一個類是否無用須要如下條件:

  1. 該類全部的實例都已經被回收, Java堆中不存在該類的任何實例;
  2. 該類對應的Class對象沒有在任何地方被引用(也就是在任何地方都沒法經過反射訪問該類的方法);
  3. 加載該類的ClassLoader已經被回收。

但即便知足以上條件也未必必定會回收, Hotspot VM還提供了-Xnoclassgc參數控制(關閉CLASS的垃圾回收功能).

參考:cloud.tencent.com/developer/a…

附:HotSpot虛擬機在1.8以後已經取消了永久代,改成元空間,類的元信息被存儲在元空間中,元空間這裏不過多介紹,有興趣的同窗能夠本身瞭解下。

5. 結語

到此關於Java GC的基本概念、JVM的內存結構、GC回收基本機制都已經介紹的差很少了。有疑問或者發現文章中有錯誤的能夠在下方留言交流。像Java垃圾收集器、元空間這裏並無詳細的說明,以後有時間的話會單獨拿出來聊聊。

相關文章
相關標籤/搜索