標籤:地址 而且 資源 清理 習慣 建議 刪除 很多 bool
轉自:http://www.cnblogs.com/hzdtf/articles/5419987.html
GC:記憶體回收站,是將java的無用的堆對象進行清理,釋放記憶體,以免發生記憶體泄露。在介紹java資源回收筒前,首先介紹下幾種回收機制
1. 引用計數:
當一個對象A被其他對象B引用時,對象A引用+1,斷開引用則-1,GC工作時,會檢查所有對象中的引用計數,如果為0則代表要清除,>0則表示有其他對象引用不能清除。這種機制有一個致命缺點,就是當兩個對象互引用時,在遍曆時可能會發生這兩個對象引數永遠不為0,則永遠不會被刪除。此機制java從不使用。
2. 停止-複製:
程式暫停運行,啟動GC,GC從堆或靜態儲存區開始遍曆所有對象,判斷對象是否“活”的對象,如果是活不刪除,反之刪除。判斷是否“活”就是判斷該對象是否有被其他對象引用,從鏈上去尋找。當是活對象時,GC會從另外堆裡開避一個大空間,然後將活對象複製一份到新空間裡,在複製時按著緊密排列,同時更新所有引用地址為新的地址,不活對象原封不動。遍曆完後,再遍曆一次,這次是將不活的對象徹底給刪除。
優點:所有對象能夠重新緊密排列,不會出現記憶體片段,這對以後建立對象能提供更快的效率。
缺點:佔用的記憶體空間大,要原對象的2倍空間;這種模式無論如何所有活的對象都要複製一份,假設遍曆到最後,對象很穩定,只出現少量垃圾對象或者根本沒垃圾對象,這時已經做了複製工作,浪費了資源。
3. 標記-刪除:
程式暫停運行,啟動GC,GC從堆或靜態儲存區開始遍曆所有對象,判斷對象是否“活”的對象,如果是活不刪除,反之刪除。判斷是否“活”就是判斷該對象是否有被其他對象引用,從鏈上去尋找。當是活對象時,會給對象給個標記符號,死對象則不標記,遍曆完後,第二次遍曆時,只保留標記的對象,把所有沒標記的對象都刪除。
以上第2和第3種都是要遍曆兩次,而且都是最後一次才執行真正刪除,比起第1種來說要慢很多,因為要遍曆2次,尤其是第2種,還要複製動作,但這2種歸避了第1種的致命缺點。
早期的jvm採用的是標記-刪除模式,繼java5後,jvm採用自適應技術,就是根據自身狀態情況,在”停止-複製“和”標記-刪除“自動切換以達到最快效率。在第1遍曆時,如果確定對象需要回收,則會先執行對象的finalize()方法。
java GC具體工作原理:
JVM分配記憶體是以較大的塊為單位,如果對象為較大,則該對象本身為一個塊。GC先啟動“停止-複製”模式,遍曆對象時,遇到活對象,則把對象複製到新塊裡,同時塊代數+1,如果對象較大,則保持不動,代數+1;如果檢測到垃圾對象稀少,則自動切換到“標記-刪除”模式,如果檢測到對象分布到記憶體太零散,則又會自動切換到“停止-複製”模式來重新整理記憶體緊密分布。這就是自適應技術。
JVM分配記憶體時,有一個堆積指標,堆積指標指向當前最後一個被分配的記憶體塊,由於“停止-複製”模式,對象大部分都是連續排列的,則堆積指標移動下一格,就是尚未分配的記憶體,這時就不用在整個堆空間裡尋找未分配的記憶體了,這對建立速度有很大提高。
關於對象的finalize()方法:
finalize方法是指對象完成時執行的一些清理工作,是Object裡的受保護方法,在外界不能調用。實質上這個方法是給GC調用的,什麼時候調用以上有講。此方法是強調進一步對象需要釋放非託管對象,是一個檢測保險的作用,比如一個類裡包含訪問一個流內容,如下:
class AccessStream
{
private InputStream stream;
private boolean isClose = false;
/* (non-Javadoc)
* @see java.lang.Object#finalize()
*/
@Override
protected void finalize() throws Throwable
{
if (!isClose)
{
stream.close();
}
super.finalize();
}
}
此類在操作流完後,正確作法是,應該即時關閉流(調用close());有時因為程式員某些租心大意原因,並沒及時關閉流,這時重寫finalize() 作最後保險作用,當沒有關閉時,釋放此對象前關閉流。
通常不建議重寫finalize,除了要求程式員嚴格習慣,最重要的是finalize並不能馬上執行,即使是顯式調用System.gc() 也不保證立刻執行,只能說建議GC執行,GC是否要執行,要看當前記憶體佔用量等因素。如果finalize不能馬上執行,這就意味著本來應該早點釋放流,而出現很長時間才釋放。
java GC工作機制