1. 程式人生 > >Java垃圾回收機制(GC)

Java垃圾回收機制(GC)

什麼是垃圾(堆):

1、沒有被引用的物件就是垃圾物件

2、所有不再存活的物件

3、沒有物件引用指向原先分配給某個物件的記憶體時(百度百科)

Java棧空間垃圾回收:

1、我們定義一些基本型別的變數和對應的變數資料還有物件的引用變數,都在函式的棧記憶體中分配。當在一段程式碼塊定義一個區域性變數時,Java就在棧中為這個變數分配記憶體空間,當該變數退出該作用域後,該變數就稱為垃圾,Java會自動釋放掉為該變數所分配的記憶體空間,該記憶體空間可以立即被另作他用。 

Java堆空間垃圾回收:

Java語言規範沒有明確地說明JVM使用哪種垃圾回收演算法,但任何一種垃圾收集演算法一般要做2件基本的事情:(1)發現垃圾;(2)回收垃圾佔用的記憶體空間,使該空間可被程式再次使用。

判斷垃圾的常見演算法:

1、引用計數法(Reference Counting Collector):

沒有被引用的物件就是垃圾物件。那麼我們為每一個建立的物件都分配一個引用計數器。當物件被引用時,計數器就+1 當引用計數器為0時,就意味著該物件沒有被引用,那麼該物件就是垃圾物件。但該方案有個問題,就是無法檢測“迴圈引用”:當兩個物件互相引用,它倆的計數都不為零,即使它們物件本身都不被外界任何東西引用(垃圾),但引用計數器卻不為0,所以佔用的記憶體空間永遠不會被回收。

2、可達性分析法(tracing演算法、標記和清除(mark-and-sweep)垃圾收集器):

tracing演算法是為了解決引用計數法的問題而提出的。基本思路是把所有引用的物件想象成一棵樹,從樹的根結點(根集) GC Roots 出發,持續遍歷找出所有連線的樹枝物件,這些物件則被稱為“可達”物件,或稱“存活”物件。其餘的物件則被視為“死亡”的“不可達”物件,或稱“垃圾”。

那麼什麼物件可以是GC Roots?

GC Roots 本身一定是可達的,這樣從它們出發遍歷到的物件才能保證一定可達。那麼,Java 裡有哪些物件是一定可達呢?

2.1、虛擬機器棧(幀棧中的本地變量表)中引用的物件

2.2、方法區中靜態屬性引用的物件

2.3、方法區中常量引用的物件

2.4、本地方法棧JNI引用的物件

GC Roots涉及到 JVM 本身的記憶體結構,先簡單知道有這麼幾種型別的 GC Roots 每次垃圾回收器會從這些根結點開始遍歷尋找所有可達節點。

垃圾的回收方式:

紅色:垃圾物件  藍色:存活物件 綠色:空白記憶體空間 

1、標記-清理:(tracing演算法)

第一步,標記:利用可達性遍歷堆記憶體,把“存活”物件和“垃圾”物件進行標記

第二步,清理:把所有“垃圾”物件所佔的空間直接清空

 

該方式簡單方便 ,但是容易產生記憶體碎片。

2、標記-整理(compacting演算法(Compacting Collector))

相同第一步,標記:利用可達性遍歷堆記憶體,把“存活”物件和“垃圾”物件進行標記

第二步,整理:清理垃圾物件時,把存活的物件整理到同一個地方

3、複製(copying演算法) 

把堆記憶體分成兩部分,一段時間內只允許在其中一部分記憶體上進行分配,當這塊記憶體被分配完後,則執行垃圾回收,把所有存活 物件全部複製到另一部分記憶體上,當前部分則直接全部清空。

清理前: 

清理後: 

 

只使用上面部分的記憶體,直到記憶體使用完畢,進行垃圾回收,把所有存活物件搬到下半部分,並把上半部分全部清空。該做法不容易產生碎片(會產生碎片),同時它意味著你在一段時間內只能使用一部分的記憶體,超過這部分記憶體的話就意味著堆記憶體裡頻繁的 複製清空。這種方案適合 存活物件少,垃圾多的情況,這樣在複製時就不需要複製多少物件過去(複製物件也消耗資源),多數垃圾直接被清空處理。