首頁 >Java >java教程 >java學習之Jvm垃圾回收器(基礎篇)

java學習之Jvm垃圾回收器(基礎篇)

青灯夜游
青灯夜游轉載
2018-10-16 16:14:442249瀏覽

本篇文章就帶給大家java學習之Jvm垃圾回收器(基礎篇)介紹。有一定的參考價值,有需要的朋友可以參考一下,希望對你們有幫助。

一:概述

在這篇文章中《Jvm運行時資料區》介紹了Java記憶體運行時區域的各個部分,其中程式計數器、虛擬機棧、本地方法棧, 3個區域隨著線程的生存而生存的。記憶體分配和回收都是確定的。隨著線程的結束內存自然就被回收了,因此不需要考慮垃圾回收的問題。而Java堆和方法區則不一樣,各執行緒共享,記憶體的分配和回收都是動態的。因此垃圾收集器所關注的都是這部分記憶體。

接下來我們就討論Jvm是怎麼回收這部分記憶體的。在進行回收前垃圾收集器第一件事就是確定哪些物件還存活,哪些已經死去。下面介紹兩種基礎的回收演算法。

1.1 引用計數演算法

為物件新增一個引用計數器,每當有一個地方引用它時計數器就 1,當引用失效時計數器就-1,。只要計數器等於0的物件就是不可能再使用的。

此演算法在大部分情況下都是一個不錯的選擇,也有一些著名的應用案例。但是Java虛擬機器中是沒有使用的。

優點:實作簡單、判斷效率高。

缺點:很難解決物件之間循環引用的問題。例如下面這個例子

Object a = new Object(); 
Object b = new Object(); 
a=b; 
b=a; 
a=b=null; //这样就导致gc无法回收他们。  

1.2 可達性分析演算法

透過一系列的稱為「GC Roots」的物件作為起始點,從這些節點開始向下搜索,搜尋所走過的路徑稱為引用鏈,當一個物件到GC Roots沒有使用任何引用鏈時,則表示該物件是不可用的。

主流的商用程式語言(Java、C#等)在主流的實作中,都是透過可達性分析來判定物件是否存活的。

透過下圖來清晰的感受gc root與物件所展示的連結。所示灰色區域物件是存活的,Object5/6/7皆是可回收的物件

  

在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 判斷一個對象生存還是死亡

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

1、第一次標記

如果物件進行可達性分析演算法之後沒發現與GC Roots相連的引用鏈,那它將會第一次標記並且進行一次篩選。

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

篩選結果:當物件沒有覆寫finalize()方法、或finalize()方法已經被JVM執行過,則判定為可回收物件。如果物件有必要執行finalize()方法,則被放入F-Queue佇列中。稍後在JVM自動建立、低優先權的Finalizer執行緒(可能多個執行緒)中觸發這個方法;  

2、第二次標記

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

如果物件在finalize()方法中重新與參考鏈上的任何一個物件建立了關聯,那麼二次標記時則會將它移出「即將回收」集合。如果此時對象還沒成功逃脫,那麼只能被回收了。

3、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影片教學java開發圖文教學bootstrap影片教學

以上是java學習之Jvm垃圾回收器(基礎篇)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除

相關文章

看更多