【JVM】垃圾收集器

程序計數器、Java虛擬機棧、本地方法棧分配的內存是肯定的,生命週期與線程一樣。因此不需要過多考慮回收問題。算法

而Java堆和方法區僅僅有運行時才知道有哪些對象被建立,需要多少內存,這部分的內存分配和回收是動態的。數組


1. 檢測垃圾內存的方法

1) 引用計數器

給對象加入引用計數器,有地方引用時+1,引用失效時-1。不論什麼時刻計數器爲0的對象就是不可能在被使用的。但是!markdown

不能解決對象間互相引用的問題,因此主流虛擬機不用這種方法。post

2) 可達性分析算法

經過一系列稱爲「GCRoots」的對象做爲起始點,開始向下搜索,走過的路徑稱爲引用鏈,當一個對象到GCRoots沒有不論什麼引用鏈相連時,則該對象不可用。
可做爲GCRoots的對象包含:線程

  • 虛擬機棧中引用的對象
  • 方法區中類靜態屬性應用的對象
  • 方法區中常量引用的對象
  • 本地方法棧中JNI引用的對象

2. 對象死亡過程

至少要經歷兩次標記過程:指針

  • 可達性分析後不可達的對象被第一次標記並且進行一次篩選,篩選條件是對象是否有必要運行finalize()方法。當對象沒有覆蓋finalize()方法或者已經調用過,則沒有必要運行。對象會被放進「即將回收」集合;有必要運行的對象會被放在一個叫F-Queue的隊列中,會由本身主動創建的低優先級的Finalizer線程去觸發,但不保證能運行結束。
  • 在finalize()方法中對象將本身與引用鏈上的不論什麼一個對象關聯起來,則GC在F-Queue中進行第二次小規模標記時,這些對象會被移出回收集合。因此運行finalize()的對象不必定會被回收。

    不論什麼對象的finalize()方法僅僅能被調用一次,因此第一次逃脫後第二次將沒法逃脫。對象


3. 回收方法區

主要回收廢棄常量和沒用的類。生命週期

  • 廢棄常量:沒有其它地方被引用到的常量。
  • 沒用的類:知足3個條件即可以回收而並不必定:
    • 該類的所有實例都被回收
    • 載入該類的ClassLoader已經被回收
    • 該類相應的Class對象沒有在不論什麼地方被引用

4. 垃圾收集算法

1) 「標記-清除」算法:

這裏寫圖片描寫敘述
不足:隊列

  • 效率問題。標記和清除過程的效率都不高。

  • 空間問題。產生大量不連續的碎片,致使載入較大對象時要提早出發下一次垃圾收集過程。

2) 複製算法

將內存分爲相等大小的兩塊。每次僅僅使用當中一塊。當一塊用完時將活着的對象拷貝到還有一塊上面。而後一次清除使用過的那塊內存。圖片


這裏寫圖片描寫敘述
長處:僅僅要移動堆頂指針。按順序分配內存就能夠。實現簡單。運行高效。
缺點:內存縮小爲原來的一半。在對象存活率高的時候不適用,適合新生代。


IBM策略:採用一個較大的Eden空間(80%)和兩個較小的Survivor空間(10%),每次使用Eden和一個Survivor。

3) 「標記-整理」算法:

適用於老年代,標記後讓所有存活的對象都向一端移動。而後直接清理掉端邊界之外的內存。


這裏寫圖片描寫敘述

4) 分代收集算法:

將Java堆分爲新生代和老年代。依據各個年代的特色採用最適合的收集算法。


5. 內存分配與回收策略

  • 對象優先在新生代的Eden分配
  • 大對象直接進入老年代(很是長的字符串、數組….)
  • 長期存活的對象進入老年代。每個對象都有一個年齡計數器。在Eden中出生並經歷第一次GC,存活後能被Survivor容納,則年齡置爲1,每在Survivor中熬過一次GC,年齡+1。年齡增大到必定程度(默認15)則會被晉升到老年代。
相關文章
相關標籤/搜索