java中的垃圾處理機制

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虛擬機在未面臨記憶體耗盡的情況下,不會浪費時間去執行垃圾回收以恢復記憶體的。