GC回收是按照五種算法實現的
1.引用計數算法
效率較高,但是無法回收循環引用的對象
2.根搜索算法
它的處理方式就是,設立若干種根對象,當任何一個根對象到某一個對象均不可達時,則認爲這個對象是可以被回收的。
GC root有幾下種:
- Class - 由系統類加載器(system class loader)加載的對象,這些類是不能夠被回收的,他們可以以靜態字段的方式保存持有其它對象。我們需要注意的一點就是,通過用戶自定義的類加載器加載的類,除非相應的java.lang.Class實例以其它的某種(或多種)方式成爲roots,否則它們並不是roots,.
- Thread - 活着的線程
- Stack Local - Java方法的local變量或參數
- JNI Local - JNI方法的local變量或參數
- JNI Global - 全局JNI引用
- Monitor Used - 用於同步的監控對象
- Held by JVM - 用於JVM特殊目的由GC保留的對象,但實際上這個與JVM的實現是有關的。可能已知的一些類型是:系統類加載器、一些JVM知道的重要的異常類、一些用於處理異常的預分配對象以及一些自定義的類加載器等。然而,JVM並沒有爲這些對象提供其它的信息,因此需要去確定哪些是屬於"JVM持有"的了。
3.標記/清除算法
標記:標記的過程其實就是,遍歷所有的GC Roots,然後將所有GC Roots可達的對象標記爲存活的對象。
清除:清除的過程將遍歷堆中所有的對象,將沒有標記的對象全部清除掉。
這種算法的缺點就是效率比較低且清理出來的空閒內存是不連續的
4.複製算法
複製算法將內存劃分爲兩個區間,在任意時間點,所有動態分配的對象都只能分配在其中一個區間(稱爲活動區間),而另
外一個區間(稱爲空閒區間)則是空閒的。
當有效內存空間耗盡時,JVM將暫停程序運行,開啓複製算法GC線程。接下來GC線程會將活動區間內的存活對象,全部
複製到空閒區間,且嚴格按照內存地址依次排列,與此同時,GC線程將更新存活對象的內存引用地址指向新的內存地址。
此時,空閒區間已經與活動區間交換,而垃圾對象現在已經全部留在了原來的活動區間,也就是現在的空閒區間。事實上,
在活動區間轉換爲空間區間的同時,垃圾對象已經被一次性全部回收。
這種算法的缺點就是內存消耗大,浪費了一半的內存
5.標記/整理算法
標記:它的第一個階段與標記/清除算法是一模一樣的,均是遍歷GC Roots,然後將存活的對象標記。
整理:移動所有存活的對象,且按照內存地址次序依次排列,然後將末端內存地址以後的內存全部回收。因此,第二階段
才稱爲整理階段。
標記/整理算法唯一的缺點就是效率也不高
總結:
後面三種算法是根據根搜索算法判斷是否應該被回收,而支撐根搜索算法可以正常工作的理論依據,就是語法中變量作用域的相關內容。因此要想防止內存泄露,最根本的辦法就是掌握好變量作用域。