標籤:
Java的記憶體回收機制筆記java記憶體回收的意義
- 確保不再被引用的對象的記憶體空間被回收。
- 確保被引用的對象的記憶體不被錯誤回收。
- 再分配記憶體。
java記憶體回收的常用方法引用計數收集器
堆中的每個對象(不是對象的引用)都有一個引用計數。當一個對象被建立時,給該對象分配一個變數,該變數計數設定設定為1.當任何其他變數被賦值為這個對象的引用,計數加1(a=b,則b引用的對象計數+1),但當一個對象的某個引用超過生命週期或者被設定為一個新值的時候,引用的計數減1(a=c,則a不再指向b指向的對象,而指向c引用指向的對象,所以b引用的對象計數-1,而c的+1).任何引用計數為0的對象可以被當作記憶體回收。當一個對象被記憶體回收時,它引用的任何對象計數減1(a=null,此時c引用的對象計數-1)。
優點:引用計數收集器可以快速執行。 缺點:無法檢測出循環參考。例如
class Main{ public static void main(String[] args){ Person zhang = new Person(); Person wang = new Person(); zhang.object = wang; wang.object = zhang; zhang = null; wang = null; }}class Person{ public Object object = null;}
雖然zhang和wang兩個對象最後已經被賦值為null,zhang和wang所指向的對象已經不會被訪問,但是由於zhang和wang互相應用對方,所以二者的引用計數並不為0.
對象引用遍曆(跟蹤搜集器)
對象引用從一組對象開始,沿著整個對象圖上的每條連結,遞迴確定可到達(reachable)的對象。如果某個對象不能從這些根對象的一個(至少一個)到達,則把它作為記憶體回收。會有三個過程,
- 標記(marking)對象,標記哪些對象可以到達。
- 清除(sweeping)不可達到的對象。
- 壓縮(compacting)剩餘的對象,重新組織記憶體中的對象,形成可以利用的記憶體空間。
典型的垃圾收集演算法Mark-Sweep(標記-清除)演算法
分成兩個階段,第一個階段是,標記所有被回收的對象;第二個階段是,回收被標記對象所佔用的空間。
該演算法的弊端是,易產生記憶體片段。
Copying(複製)演算法
為瞭解決Mark-Sweep演算法的弊端,複製演算法把記憶體按照容量劃分為大小相等的兩塊,每次只是用其中的一塊。但這一塊記憶體用完了,就將還存活的對象複製到另外一塊上面,然後把已經使用的記憶體空間一次清理掉,這樣不易出現記憶體片段。
不易產生記憶體片段,弊端也顯而易見,記憶體利用率太低,如果存活對象很多,效率也會很低。
Mark-Compact(標記整理)演算法
為瞭解決Copying演算法的弊端,標記整理演算法提出了。該演算法標記階段與Mark-Sweep一樣,但是在完成目標標記之後,不是直接清理可回收對象,而是將存活對象向一端移動,然後清理掉端邊界以外的記憶體。
比較理想的演算法了。
Generational Collection(分代收集)演算法
主流演算法。核心思想是根據對象存活的生命週期將記憶體劃分為若干個不同的地區。一般情況下,將堆劃分為老年代(Tenured Generation)和新生代(Young Generation)。老年代每次記憶體回收時只有少量對象需要回收,而新生代每次記憶體回收時都有大量對象需要被回收。
新生代一般採取Copying演算法。而且實際上並不是按照1:1的比例劃分新生代空間的。而是劃分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden空間和其中的一塊Survivor空間,回收時,將Eden空間和Survivor中還存活的對象複製到另一塊Survivor空間內,然後清理掉Eden空間和剛才使用的Survivor空間。
老年代一般使用Mark-Compact演算法。
與java記憶體回收相關的方法System.gc()
使用System.gc()可以不管JVM使用的是哪一種記憶體回收演算法,都可以請求記憶體回收。但是調用了System.gc()只是向JVM提出了記憶體回收的請求,JVM接受這個訊息之後,並不一定立即做記憶體回收,而是對幾個記憶體回收演算法做加權,使記憶體回收更加容易發生,或提早發生,或回收較多。
finalize()方法
在JVM記憶體回收行程收集一個對象之前,一般會要求程式調用適當的方法釋放資源,但是在沒有明確釋放資源的情況下,java提供了預設機制來中止該對象釋放資源,這個方法就是finalize()方法。它的原型是:
protected void finalize() throws Throwable
在finalize()方法返回之後,記憶體回收開始執行。
使用fianlize()的原因是存在著記憶體回收行程不能處理的特殊情況:
- native method中調用了C/C++的malloc()方法,但是又沒有調用free函數。
- 開啟的檔案資源。
參考資料:
java記憶體回收機制
java記憶體回收機制
詳細介紹java記憶體回收機制
Java的記憶體回收機制筆記