JVM垃圾回收重要理論剖析【純理論】

JVM學習到這裏,終於到學習最興奮的地方了---垃圾回收,在學習它以前還得對JVM垃圾回收相關理論知識進行了解,而後再經過實踐來加深對理論的理解,下面直接開始瞭解相關的理論:html

JVM運行時內存數據區域:java

這個在以前其實已經介紹過了,對於JVM的垃圾回收必定是回收內存裏面的內容,因此若是不對內存區域的劃分,區域存放的內容有所瞭解,那何談垃圾回收呢?因此看一下下圖對內存區域的劃分描述:web

其這以上區域在上一次【http://www.javashuo.com/article/p-xzdzxdar-do.html】都已經講過了,下面再來總體回顧一下。算法

方法區域:框架

其中它是數據是線程共享的:學習

那所謂方法區域是線程共享的是指的啥意思呢?好比說一個類的class元信息就會映射到方法區域當中,那麼這個元信息會被全部的線程所訪問,由於只有一份,因此該區域是線程共享的。spa

下面再來闡述一下它:線程

  • 存放了每一個Class的結構信息,包括常量池、字段描述、方法描述。
  • GC的非主要工做區域。

Java虛擬機棧(JVM Stack):3d

下面再對其進行一些闡述:代理

  • Java虛擬機描述的是Java方法的執行模型:每一個方法執行的時候都會建立一個幀(Frame),棧用於存放局部變量表,操做棧,動態連接,方法出口等信息。一個方法的執行過程,就是這個方法對於棧幀的入棧出棧過程。
  • 它是線程隔離的,如圖上所示。

本地方法棧:

 

堆:

 它裏面的數據也是線程共享的:

下面再來闡述一下它:

  • 堆裏存放的是對象的實例。
  • 是Java虛擬機管理內存中最大的一塊。
  • GC主要的工做區域,爲了高效的GC,會把堆細分更多的子區域。【這個在以後會細說】
  • 它是線程共享的,如圖所示。

程序計數器: 

JVM運行時數據區域例子:

對於這樣一個方法代碼:

以上方法在執行以後在內存中發生的變化以下:

  • 生成了2部分的內存區域:一、obj這個引用變量,由於是方法內的變量,放到JVM Stack裏面;二、真正Object class的實例對象,放到Heap裏面。
  • 上述的new語句一共消耗12個bytes,JVM規定引用佔4個bytes(在JVM Stack),而空對象是8個bytes(在Heap)。
  • 方法結束後,對應Stack中的變量立刻回收,可是Heap中的對象要等到GC來回收。

JVM垃圾回收(GC)模型:

垃圾判斷算法:

  • 引用計數算法(Reference Counting)
    ①、給對象添加一個引用計數器,當有一個地方引用它,計數器加1,當引用失效,計數器減1,任什麼時候刻計數器爲0的對象就是不可能再被使用的,比較好理解。
    ②、引用計數算法沒法解決對象循環引用的問題。啥意思,下面用圖來講明一下:

    JVM中存在A、B兩個對象,而A、B是相互引用着的,也就是A裏面持有B的引用,而B裏面又持有A的引用,以下:

    而開始這兩對象是被其它地方所引用着的,好比方法棧中,以下:


    而以後虛擬機棧的這兩個引用消失了,也就是整個虛擬機中就只有這兩個相互引用的對象了,而這兩對象不被任何對象所引用着了:

    而根據引用計數器的定義規則,A和B的引用計數器都是1,可是實際這倆都是孤立的對象,因此若是採用引用計數來進行垃圾回收,則這倆對象永遠不會被回收。

  • 根搜索算法(GC Roots Tracing) 
    既然引用計數算法存在對象循環引用的問題,因此此算法出現了,下面具體看下該算法:
    ①、在實際的生產語言中(Java、C#等),都是使用根搜索算法判斷對象是否存活。
    ②、算法基本思路就是經過一系列的稱爲「GC Roots」的點做爲超始進行向下搜索,當一個對象到GC Roots沒有任何引用鏈(Reference Chain)相連,則證實此對象是不可用的。 
    ③、在Java語言中,GC Roots包括【也就是什麼是GC Roots?】:
         a、在vm棧(幀中的本地變量)中的引用。
              因此根據根搜索算法,因爲目前沒有棧中的引用指向A和B對象,因此這個算法規則這倆對象是能夠被回收的了,以下:
              
         b、方法區中的靜態引用。
         c、JNI(既通常說的Native方法)中的引用。

方法區:

  • Java虛擬機規範表示能夠不要求虛擬機在這區域實現GC,這區域GC的「性價比」通常比較低。
  • 在堆中,尤爲是在新生代,常規應用進行一次GC通常能夠回收70%~80%的空間,而方法區的GC效率遠小於此。
  • 當前的商業JVM都有實現方法區的GC,主要是回收兩部份內容:廢棄常量與無用類。
  • 類加收須要知足以下3個條件【條件是極其苛刻的】:
    一、該類全部的實例都已經被GC,也就是JVM中不存在該Class的任何實例。
    二、加載該類的ClassLoader已經被GC。
    三、該類對應的java.lang.Class對象沒有在任何地方被引用,如不能在任何地方經過反射訪問該類的方法。
  • 在大量使用反射、動態代理、CGLib等字節碼框架、動態生成JSP以及OSGi這類頻繁自定義ClassLoader的場景都須要JVM具有類卸載的支持以保證方法區不會溢出。

JVM常見GC算法:

  • 標記-清除算法(Mark-Sweep)
  • 標記-整理算法(Mark-Compact)
  • 複製算法(Copying)
  • 分代算法(Generational)

具體每一個算法下次繼續再來學習~~ 

相關文章
相關標籤/搜索