Jvm垃圾回收器(基礎篇)

一:概述

  在這篇文章中《Jvm運行時數據區》介紹了Java內存運行時區域的各個部分,其中程序計數器、虛擬機棧、本地方法棧,3個區域隨着線程的生存而生存的。內存分配和回收都是肯定的。隨着線程的結束內存天然就被回收了,所以不須要考慮垃圾回收的問題。而Java堆和方法區則不同,各線程共享,內存的分配和回收都是動態的。所以垃圾收集器所關注的都是這部份內存。html

  接下來咱們就討論Jvm是怎麼回收這部份內存的。在進行回收前垃圾收集器第一件事情就是肯定哪些對象還存活,哪些已經死去。下面介紹兩種基礎的回收算法。java

1.1 引用計數算法

  給對象添加一個引用計數器,每當有一個地方引用它時計數器就+1,當引用失效時計數器就-1,。只要計數器等於0的對象就是不可能再被使用的。算法

  此算法在大部分狀況下都是一個不錯的選擇,也有一些著名的應用案例。可是Java虛擬機中是沒有使用的。數據結構

  優勢:實現簡單、判斷效率高。post

  缺點:很難解決對象之間循環引用的問題。例以下面這個例子url

Object a = new Object(); 
Object b = new Object(); 
a=b; 
b=a; 
a=b=null; //這樣就致使gc沒法回收他們。  

1.2 可達性分析算法

  經過一系列的稱爲「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有使用任何引用鏈時,則說明該對象是不可用的。spa

  主流的商用程序語言(Java、C#等)在主流的實現中,都是經過可達性分析來斷定對象是否存活的。線程

  經過下圖來清晰的感覺gc root與對象展現的聯繫。所示灰色區域對象是存活的,Object5/6/7均是可回收的對象htm

  

 在Java語言中,可做爲GC Roots 的對象包括下面幾種對象

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象
  • 方法區中靜態變量引用的對象
  • 方法區中常量引用的對象
  • 本地方法棧(即通常說的 Native 方法)中JNI引用的對象

 優勢:更加精確和嚴謹,能夠分析出循環數據結構相互引用的狀況;

 缺點:實現比較複雜、須要分析大量數據,消耗大量時間、分析過程須要GC停頓(引用關係不能發生變化),即停頓全部Java執行線程(稱爲"Stop The World",是垃圾回收重點關注的問題)。

二:引用

 在jdk1.2以後,Java對引用的概念進行了擴充,整體分爲4類:強引用、軟引用、弱引用、虛引用,這4中引用強度依次逐漸減弱。

  • 強引用:指在代碼中廣泛存在的,相似 Object obj = new Object(); 這類的引用,只有強引用還存在,GC就永遠不會收集被引用的對象
  • 軟引用:指一些還有用但並不是必須的對象。直到內存空間不夠時(拋出OutOfMemoryError以前),纔會被垃圾回收。採用SoftReference類來實現軟引用
  • 弱引用:用來描述非必須對象。當垃圾收集器工做時就會回收掉此類對象。採用WeakReference類來實現弱引用。
  • 虛引用:一個對象是否有虛引用的存在,徹底不會對其生存時間構成影響, 惟一目的就是能在這個對象被回收時收到一個系統通知, 採用PhantomRenference類實現

 2.1 判斷一個對象生存仍是死亡

  宣告一個對象死亡,至少要經歷兩次標記。

  一、第一次標記

  若是對象進行可達性分析算法以後沒發現與GC Roots相連的引用鏈,那它將會第一次標記而且進行一次篩選。

  篩選條件:判斷此對象是否有必要執行finalize()方法。

  篩選結果:當對象沒有覆蓋finalize()方法、或者finalize()方法已經被JVM執行過,則斷定爲可回收對象。若是對象有必要執行finalize()方法,則被放入F-Queue隊列中。稍後在JVM自動創建、低優先級的Finalizer線程(可能多個線程)中觸發這個方法;  

 

  二、第二次標記

  GC對F-Queue隊列中的對象進行二次標記。

  若是對象在finalize()方法中從新與引用鏈上的任何一個對象創建了關聯,那麼二次標記時則會將它移出「即將回收」集合。若是此時對象還沒成功逃脫,那麼只能被回收了。

  三、finalize() 方法

  finalize()是Object類的一個方法、一個對象的finalize()方法只會被系統自動調用一次,通過finalize()方法逃脫死亡的對象,第二次不會再調用;

  特別說明:並不提倡在程序中調用finalize()來進行自救。建議忘掉Java程序中該方法的存在。由於它執行的時間不肯定,甚至是否被執行也不肯定(Java程序的不正常退出),並且運行代價高昂,沒法保證各個對象的調用順序(甚至有不一樣線程中調用)。

三:回收方法區  

  永久代的垃圾收集主要分爲兩部份內容:廢棄常量和無用的類

3.1 回收廢棄常量

  回收廢棄常量與Java堆的回收相似。下面舉個栗子說明

  假如一個字符串「abc」 已經進入常量池中,但當前系統沒有一個string對象是叫作abc的,也就是說,沒有任何string對象的引用指向常量池中的abc常量,也沒用其餘地方引用這個字面量。若是這是發生內存回收,那麼這個常量abc將會被清理出常量池。常量池中的其餘類(接口)、方法、字段的符號引用也與此相似。

3.2 回收無用的類

  須要同時知足下面3個條件的才能算是無用的類。

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

  虛擬機能夠對同時知足這三個條件的類進行回收,但不是必須進行回收的。是否對類進行回收,HotSpot虛擬機提供了-Xnoclassgc參數進行控制。

 

----對《深刻理解Java虛擬機》第3章垃圾收集器與內存分配策略 3.2小節總結。接下來總結3.3小結垃圾收集算法。

相關文章
相關標籤/搜索