垃圾回收
垃圾回收回調(diào)方法:
- finalize()函數(shù)是在JVM回收內(nèi)存時執(zhí)行的诗充,但JVM并不保證在回收內(nèi)存時一定會調(diào)用finalize()会烙。
JVM的垃圾回收機制:
- 在內(nèi)存充足的情況下店归,顯式調(diào)用System.gc()(system.gc調(diào)用僅僅是建議虛擬機進行回收趁曼,并不一定馬上會進行g(shù)c)
- 在內(nèi)存不足的情況下颂碘,垃圾回收將自動運行
對象狀態(tài)
可達狀態(tài):
有一個以上的引用變量引用此對象可恢復(fù)狀態(tài):
如果程序中某個對象不再有任何的引用變量引用它,它將先進入可恢復(fù)狀態(tài),系統(tǒng)的垃圾回收機制準備回收該對象的所占用的內(nèi)存返敬,在回收之前遂庄,系統(tǒng)會調(diào)用finalize()方法進行資源清理,如果資源整理后重新讓一個以上引用變量引用該對象劲赠,則這個對象會再次變?yōu)榭蛇_狀態(tài),否則就會進入不可達狀態(tài)涛目。不可達狀態(tài):
當對象的所有關(guān)聯(lián)都被切斷,且系統(tǒng)調(diào)用finalize()方法進行資源清理后依舊沒有使該對象變?yōu)榭蛇_狀態(tài)凛澎,則這個對象將永久性失去引用并且變成不可達狀態(tài)霹肝,系統(tǒng)才會真正的去回收該對象所占用的資源。
引用
級別: 強引用 > 軟引用 > 弱引用 > 虛引用
StrongReference
默認引用實現(xiàn),當沒有任何對象指向它時预厌,GC執(zhí)行后將會被回收
Food food = new Food();
food = null;
WeakReference
所引用的對象在JVM內(nèi)不再有強引用時,GC執(zhí)行后將會被回收
處理過程:
- WeakReference對象的referent域被設(shè)置為null阿迈,從而使該對象不再引用heap對象元媚。
- WeakReference引用過的heap對象被聲明為finalizable轧叽。
- 同時或者一段時間后WeakReference對象被添加到它的ReferenceQueue(如果ReferenceQueue存在的話)。
對于軟引用和弱引用刊棕,入隊和finalize方法的執(zhí)行是沒有固定順序的
Food food = new Food();
WeakReference<Food> weakFood = new WeakReference<Food>(food);
food = null;
使用:隨時取得某對象的信息(不影響此對象的垃圾收集)
A obj = new A();
WeakReference wr = new WeakReference(obj);
obj = null;
//等待一段時間炭晒,obj對象就會被垃圾回收
...
if (wr.get()==null) {
System.out.println("obj 已經(jīng)被清除了 ");} else {
System.out.println("obj 尚未被清除,其信息是 "+obj.toString());
}
SoftReference
類似WeakReference,但SoftReference會盡可能長的保留引用直到JVM內(nèi)存不足時才會被回收, 適合緩存應(yīng)用
處理過程同WeakReference
Food food = new Food();
SoftReference<Food> softFood = new SoftReference<Food>(food);
food = null;
// JVM OutOfMemory
使用:簡單對象cache
A obj = new A();
SoftRefenrence sr = new SoftReference(obj);
//引用時
if(sr!=null){
obj = sr.get();
}else{
obj = new A();
sr = new SoftReference(obj);
}
PhantomReference
跟蹤referent何時被enqueue到ReferenceQueue中,它唯一的目的就是對象被回收時能收到一個通知甥角,用于追蹤對象被垃圾回收的狀態(tài)网严,需要和引用隊列ReferenceQueue類聯(lián)合使用。
不建議使用,有潛在的內(nèi)存泄露風(fēng)險,因為JVM不會自動幫助我們釋放,我們必須要保證它指向的堆對象是不可達的
虛引用帶來的內(nèi)存泄露風(fēng)險參考:java中虛引用PhantomReference與弱引用WeakReference(軟引用SoftReference)的差別嗤无。
軟引用和弱引用差別不大震束,JVM都是先將其referent字段設(shè)置成null,之后將軟引用或弱引用当犯,加入到關(guān)聯(lián)的引用隊列中垢村。我們可以認為JVM先回收堆對象占用的內(nèi)存,然后才將軟引用或弱引用加入到引用隊列嚎卫。
而虛引用則不同嘉栓,JVM不會自動將虛引用的referent字段設(shè)置成null,而是先保留堆對象的內(nèi)存空間拓诸,直接將PhantomReference加入到關(guān)聯(lián)的引用隊列侵佃,也就是說如果我們不手動調(diào)用PhantomReference.clear(),虛引用指向的堆對象內(nèi)存是不會被釋放的奠支。
處理過程:
- 不把referent設(shè)置為null.
- PhantomReference引用過的heap對象處理到finalized狀態(tài),即調(diào)用了finalize()方法.
- heap對象被釋放之前把PhantomRefrence對象添加到它的ReferenceQueue中.
摘錄部分蓋樓評論:
我覺得其實是這樣馋辈,其實GC做的工作分成是兩部分,第一部分是將對象從finalizable狀態(tài)到finalized狀態(tài)倍谜,這只是完成了資源的釋放首有;第二部分是reclaimed對象占用的內(nèi)存燕垃。其實所有在ref中的三種reference的referent的對象的reclaimed都只有在相應(yīng)reference對象的clear方法調(diào)用之后,才能進行井联,所以卜壕,GC只是保證weakreference、softreference的clear方法被GC自動調(diào)用烙常,并被加到referencequeue中轴捎,但是phantomreference對象在被加入到referencequeue中之前對象就已經(jīng)被GC finalied(如果定義了finalize方法的話,我所指的finalized是指調(diào)用了finalize方法)了蚕脏,只是還沒有進行第二步reclaimed侦副,因為phantomreference對象的clear方法還沒有被調(diào)用,所以不能進行reclaimed驼鞭。
Food food = new Food();
PhantomReference<Food> phantomFood = new PhantomReference<Food>(food, new ReferenceQueue<Food>());
food = null;
使用:
- Object的finalize方法在回收之前調(diào)用秦驯,若在finalize方法內(nèi)創(chuàng)建此類的強引用會導(dǎo)致對象無法回收,可能引發(fā)JVM OutOfMemory
- PhantomReference在finalize方法執(zhí)行后進行回收挣棕,避免上述問題
??參考網(wǎng)頁關(guān)于虛引用PhantomReference
@SuppressWarnings("static-access")
public static void main(String[] args) throws Exception {
String abc = new String("abc");
System.out.println(abc.getClass() + "@" + abc.hashCode());
final ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
new Thread() {
public void run() {
while (isRun) {
Object obj = referenceQueue.poll();
if (obj != null) {
try {
Field rereferent = Reference.class
.getDeclaredField("referent");
rereferent.setAccessible(true);
Object result = rereferent.get(obj);
System.out.println("gc will collect:"
+ result.getClass() + "@"
+ result.hashCode() + "\t"
+ (String) result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}.start();
PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,
referenceQueue);
abc = null;
Thread.currentThread().sleep(3000);
System.gc();
Thread.currentThread().sleep(3000);
isRun = false;
}
總結(jié)
- 強引用指向的對象如果被引用,發(fā)生GC時是不會被回收的译隘,除非該對象沒有被引用
- 軟引用在內(nèi)存非常緊張的時候會被回收(無引用),其他時候不會被回收洛心,所以在使用之前要判斷是否為null從而判斷他是否已經(jīng)被回收了
- 弱引用和虛引用指向的對象(無引用)在發(fā)生GC時一定會被回收
- 通過虛引用得不到引用的對象實例,虛引用的get()方法永遠返回null
參考
Java的內(nèi)存回收機制
Java中三個引用類SoftReference 固耘、 WeakReference 和 PhantomReference的區(qū)別
Java引用
引用
Java幽靈引用的作用