《JAVA虛擬機》讀書筆記二之GC

1. 概述

說起垃圾收集(Garbage Collection,GC),大部分人都把這項技術當做Java語言的伴生產物。事實上,GC的歷史比Java久遠,1960年誕生於MIT的Lisp是第一門真正使用內存動態分配和垃圾收集技術的語言。當Lisp還在胚胎時期時,人們就在思考GC需要完成的3件事情:

  • 哪些內存需要回收?
  • 什麼時候回收?
  • 如何回收?

上一章介紹了Java內存運行時區域的各個部分,其中程序計數器虛擬機棧、**本地方法棧**3個區域隨線程而生,隨線程而滅;棧中的棧幀隨着方法的進入和退出而有條不紊地執行着出棧和入棧操作。每一個棧幀中分配多少內存基本上是在類結構確定下來時就已知的(儘管在運行期會由JIT編譯器進行一些優化,但在本章基於概念模型的討論中,大體上可以認爲是編譯期可知的),因此這幾個區域的內存分配和回收都具備確定性,在這幾個區域內就不需要過多考慮回收的問題,因爲方法結束或者線程結束時,內存自然就跟隨着回收了。

Java堆和方法區則不一樣,一個接口中的多個實現類需要的內存可能不一樣,一個方法中的多個分支需要的內存也可能不一樣,我們只有在程序處於運行期間時才能知道會創建哪些對象,這部分內存的分配和回收都是動態的,垃圾收集器所關注的是這部分內存,本章後續討論中的“內”分配與回收也僅指這一部分內存。

在堆裏面存放着Java世界中幾乎所有的對象實例,垃圾收集器在對堆進行回收前,第一件事情就是要確定這些對象之中哪些還“存活”着,哪些已經“死去”(即不可能再被任何途徑使用的對象)。

GC中的幾個概念

  1. 引用計數算法
    很多教科書判斷對象是否存活的算法是這樣的:給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器爲0的對象就是不可能再被使用的。
    客觀地說,引用計數算法(Reference Counting)的實現簡單,判定效率也很高,在大部分情況下它都是一個不錯的算法。但是,至少主流的Java虛擬機裏面沒有選用引用計數算法來管理內存,其中最主要的原因是它很難解決對象之間相互循環引用的問題。
  2. 可達性分析算法
    在主流的商用程序語言(Java、C#,甚至包括前面提到的古老的Lisp)的主流實現中,都是稱通過可達性分析(Reachability Analysis)來判定對象是否存活的。這個算法的基本思路就是通過一系列的稱爲“GC Roots”的對象作爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連(用圖論的話來說,就是從GC Roots到這個對象不可達)時,則證明此對象是不可用的。
    在Java語言中,可作爲GC Roots的對象包括下面幾種:

    • 虛擬機棧(棧幀中的本地變量表)中引用的對象

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

    • 方法區中常量引用的對象

    • 本地方法棧中JNI(即一般說的Native方法)引用的對象

  3. 引用
    無論是通過引用計數算法判斷對象的引用數量,還是通過可達性分析算法判斷對象的引用鏈是否可達,判定對象是否存活都與“引用”有關。

    在JDK 1.2以前,Java中的引用的定義很傳統:如果reference類型的數據中存儲的數值代表的是另外一塊內存的起始地址,就稱這塊內存代表着一個引用。
    在JDK 1.2之後,Java對引用的概念進行了擴充,將引用分爲強引用、軟引用、弱引用、虛引用4種,這4種引用強度依次逐漸減弱。

    • 強引用就是指在程序代碼之中普遍存在的,類似“Object obj=new
      Object()”這類的引用,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。

    • 軟引用是用來描述一些還有用但並非必需的對象。對於軟引用關聯着的對象,在系統將要發生內存溢出異常之前,將會把這些對象列進回收範圍之中進行第二次回收。如果這次回收還沒有足夠的內存,纔會拋出內存溢出異常。在JDK 1.2之後,提供了SoftReference類來實現軟引用。

    • 弱引用也是用來描述非必需對象的,但是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次垃圾收集發生之前。當垃圾收集器工作時,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。在JDK 1.2之後,提供了WeakReference類來實現弱引用。

    • 虛引用也稱爲幽靈引用或者幻影引用,它是最弱的一種引用關係。一個對象是否有虛引 用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來取得一個對象實例。爲一 個對象設置虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知。在 JDK 1.2之後,提供了PhantomReference類來實現虛引用。

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