java中的垃圾處理機制

Alias_^_^發表於2020-06-02

1.何為垃圾
在Java中,如果物件實體沒有引用指向的話,儲存該實體的記憶體便成為垃圾。JVM會有一個系統執行緒專門負責回收垃圾。垃圾同時包括分配物件記憶體間的碎片塊


2.垃圾處理包含的演算法

Java語言規範沒有明確地說明JVM使用哪種垃圾回收演算法,但是任何一種垃圾回收演算法一般要做2件基本的事情:(1)發現無用的資訊物件,(2)回收無用物件佔據的記憶體,使得該記憶體可以被程式再次使用。
垃圾回收一面在回收記憶體,一面使堆中的記憶體緊密排列。下面介紹幾種演算法:


2-1引用計數法
該演算法使用引用計數器來區分存活物件和不再使用的物件。一般來說,堆中的每個物件對應一個引用計數器。當每一次建立一個物件並賦給一個變數時,引用計數器置為1。當物件被賦給任意變數時,引用計數器每次加1當物件出了作用域後(該物件丟棄不再使用)或者被置為null時,引用計數器減1,一旦引用計數器為0,物件就滿足了垃圾收集的條件。
基於引用計數器的垃圾收集器執行較快,不會長時間中斷程式執行,適宜必須實時執行的程式。但引用計數器增加了程式執行的開銷,因為每次物件賦給新的變數,計數器加1,而每次現有物件出了作用域,計數器減1。雖然管理引用計數的開銷不大,但是該開銷在整個程式的生命週期


2-2tracing演算法(標記-清除)
基於tracing演算法的垃圾收集也稱為標記和清除(mark-and-sweep)垃圾收集器,它所依據的思路是,從棧和靜態儲存區出發,遍歷所有的引用,找到存活的物件,每當找到一個存活的物件,就給該物件設一個標記,當標記工作全部完成時,清理工作才會開始。在清理的過程中,沒有標記的物件會被釋放。該方式相當慢,在產生少量垃圾和幾乎不產生垃圾的情況下速度就很快了。


2.3. compacting演算法(標記-整理)
為了解決堆碎片問題,基於compacting的垃圾回收吸收了tracing演算法的思想。在清除無用物件之後,演算法將所有的物件移到堆的一端,堆的另一端就變成了一個相鄰的空閒記憶體區,收集器會對它移動的所有物件的所有引用進行更新,使得這些引用在新的位置能識別原來的物件。解決了記憶體碎片的問題(不但進行了清理而且進行了物件的搬運,成本更高)。在基於Compacting演算法的收集器的實現中,一般增加控制程式碼和控制程式碼表。


2.4. copying演算法
它開始時把堆分成一個物件區和多個空閒區,程式從物件區為物件分配空間,當物件滿了,基於coping演算法的垃圾回收就掃描活動物件,並將每個活動物件複製到空閒區(使得活動物件所佔的記憶體之間沒有空閒間隔),這樣空閒區變成了物件區,原來的物件區變成了空閒區,程式會在新的物件區中分配記憶體。
一種典型的基於coping演算法的垃圾回收是stop-and-copy演算法,它將堆分成物件區和空閒區域區,在物件區與空閒區域的切換過程中,程式暫停執行。


2.5. generation演算法
stop-and-copy垃圾收集器的一個缺陷是收集器必須複製所有的活動物件,這增加了程式等待時間,這是coping演算法低效的原因。分代的垃圾回收策略,是基於這樣一個事實:不同的物件的生命週期是不一樣的。因此,不同生命週期的物件可以採取不同的回收演算法,以便提高回收效率。generation演算法將堆分成兩個或多個,每個子堆作為物件的一代(generation)。1.所有新生成的物件首先都是放在年輕代的。年輕代的目標就是儘可能快速的收集掉那些生命週期短的物件。2.在年輕代中經歷了N次垃圾回收後仍然存活的物件,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命週期較長的物件。3.持久代用於存放靜態檔案,如Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者呼叫一些class,在這種時候需要設定一個比較大的持久代空間來存放這些執行過程中新增的類。


3.System.gc()方法
使用System.gc()可以不管JVM使用的是哪一種垃圾回收的演算法,都可以請求Java虛擬機器進行垃圾回收,值得注意的是,JVM接受這個訊息後,並不是立即做垃圾回收(需要搶佔CPU資源),而只是對幾個垃圾回收演算法做了加權,使垃圾回收操作容易發生,或提早發生,或回收較多而已。
儘量避免顯示的呼叫gc,若不針對GC的特點進行設計和編碼,就會出現記憶體駐留等一系列負面影響。此函式建議JVM進行主GC,雖然只是建議而非一定,但很多情況下它會觸發主GC,從而增加主GC的頻率,也即增加了間歇性停頓的次數。


4. finalize()方法
java垃圾處理器只能釋放哪些經由New出來的物件記憶體,對於其它途徑產生的記憶體,java允許在類中定義finalize()方法,在垃圾回收時候呼叫finalize(),處理記憶體,雖然不一定發生,但是當垃圾回收動作發生時,非new記憶體會被清理。finalize中新增一些非java能夠處理的垃圾,例如類似C語言中使用的malloc()函式分配的記憶體,除非呼叫free(),否則記憶體得不到釋放,造成洩露。所以,在finalize方法中呼叫free()方法,(free是C和C++的方法)。當垃圾回收發生時,finalize()函式被呼叫。絕對不能直接呼叫finalize(),因為垃圾回收只與記憶體有關,無論物件是如何建立的,垃圾回收器都會負責釋放那些物件佔有的記憶體。
當垃圾回收器確定不存在對該物件的更多引用時,由物件的垃圾回收器呼叫此方法。子類重寫 finalize 方法,以配置系統資源或執行其他清除。
java虛擬機器在未面臨記憶體耗盡的情況下,不會浪費時間去執行垃圾回收以恢復記憶體的。

相關文章