強(qiáng)引用 ( Strong Reference )
強(qiáng)引用是使用最普遍的引用摊趾。如果一個對象具有強(qiáng)引用顽频,那垃圾回收器絕不會回收它肛著。當(dāng)內(nèi)存空間不足圆兵,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯誤,使程序異常終止枢贿,也不會靠隨意回收具有強(qiáng)引用的對象來解決內(nèi)存不足的問題殉农。 ps:強(qiáng)引用其實(shí)也就是我們平時A a = new A()這個意思。
- 強(qiáng)引用特性
- 強(qiáng)引用可以直接訪問目標(biāo)對象局荚。
- 強(qiáng)引用所指向的對象在任何時候都不會被系統(tǒng)回收超凳。
- 強(qiáng)引用可能導(dǎo)致內(nèi)存泄漏。
Final Reference
- 當(dāng)前類是否是finalizer類耀态,注意這里finalizer是由JVM來標(biāo)志的( 后面簡稱f類 )轮傍,并不是指java.lang.ref.Finalizer類。但是f類是會被JVM注冊到j(luò)ava.lang.ref.Finalizer類中的首装。
① 當(dāng)前類或父類中含有一個參數(shù)為空创夜,返回值為void的名為finalize的方法。
② 并且該finalize方法必須非空
- GC 回收問題
- 對象因為Finalizer的引用而變成了一個臨時的強(qiáng)引用仙逻,即使沒有其他的強(qiáng)引用驰吓,還是無法立即被回收涧尿;
- 對象至少經(jīng)歷兩次GC才能被回收,因為只有在FinalizerThread執(zhí)行完了f對象的finalize方法的情況下才有可能被下次GC回收檬贰,而有可能期間已經(jīng)經(jīng)歷過多次GC了姑廉,但是一直還沒執(zhí)行對象的finalize方法;
- CPU資源比較稀缺的情況下FinalizerThread線程有可能因為優(yōu)先級比較低而延遲執(zhí)行對象的finalize方法偎蘸;
- 因為對象的finalize方法遲遲沒有執(zhí)行庄蹋,有可能會導(dǎo)致大部分f對象進(jìn)入到old分代,此時容易引發(fā)old分代的GC迷雪,甚至Full GC限书,GC暫停時間明顯變長,甚至導(dǎo)致OOM章咧;
- 對象的finalize方法被調(diào)用后倦西,這個對象其實(shí)還并沒有被回收,雖然可能在不久的將來會被回收赁严。
詳見:JVM源碼分析之FinalReference完全解讀 - 你假笨
軟引用 ( Soft Reference )
是用來描述一些還有用但并非必須的對象扰柠。對于軟引用關(guān)聯(lián)著的對象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前疼约,將會把這些對象列進(jìn)回收范圍之中進(jìn)行第二次回收卤档。如果這次回收還沒有足夠的內(nèi)存,才會拋出內(nèi)存溢出異常程剥。
對于軟引用關(guān)聯(lián)著的對象劝枣,如果內(nèi)存充足,則垃圾回收器不會回收該對象织鲸,如果內(nèi)存不夠了舔腾,就會回收這些對象的內(nèi)存。在 JDK 1.2 之后搂擦,提供了 SoftReference 類來實(shí)現(xiàn)軟引用稳诚。軟引用可用來實(shí)現(xiàn)內(nèi)存敏感的高速緩存。軟引用可以和一個引用隊列(ReferenceQueue)聯(lián)合使用瀑踢,如果軟引用所引用的對象被垃圾回收器回收扳还,Java虛擬機(jī)就會把這個軟引用加入到與之關(guān)聯(lián)的引用隊列中。
注意:Java 垃圾回收器準(zhǔn)備對SoftReference所指向的對象進(jìn)行回收時橱夭,調(diào)用對象的 finalize() 方法之前普办,SoftReference對象自身會被加入到這個 ReferenceQueue 對象中,此時可以通過 ReferenceQueue 的 poll() 方法取到它們徘钥。
/**
* 軟引用:對于軟引用關(guān)聯(lián)著的對象衔蹲,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會把這些對象列進(jìn)回收范圍之中進(jìn)行第二次回收( 因為是在第一次回收后才會發(fā)現(xiàn)內(nèi)存依舊不充足,才有了這第二次回收 )舆驶。如果這次回收還沒有足夠的內(nèi)存橱健,才會拋出內(nèi)存溢出異常。
* 對于軟引用關(guān)聯(lián)著的對象沙廉,如果內(nèi)存充足拘荡,則垃圾回收器不會回收該對象,如果內(nèi)存不夠了撬陵,就會回收這些對象的內(nèi)存珊皿。
* 通過debug發(fā)現(xiàn),軟引用在pending狀態(tài)時巨税,referent就已經(jīng)是null了蟋定。
*
* 啟動參數(shù):-Xmx5m
*
*/
public class SoftReferenceDemo {
private static ReferenceQueue<MyObject> queue = new ReferenceQueue<>();
public static void main(String[] args) throws InterruptedException {
Thread.sleep(3000);
MyObject object = new MyObject();
SoftReference<MyObject> softRef = new SoftReference(object, queue);
new Thread(new CheckRefQueue()).start();
object = null;
System.gc();
System.out.println("After GC : Soft Get = " + softRef.get());
System.out.println("分配大塊內(nèi)存");
/**
* ====================== 控制臺打印 ======================
* After GC : Soft Get = I am MyObject.
* 分配大塊內(nèi)存
* MyObject's finalize called
* Object for softReference is null
* After new byte[] : Soft Get = null
* ====================== 控制臺打印 ======================
*
* 總共觸發(fā)了 3 次 full gc。第一次有System.gc();觸發(fā)草添;第二次在在分配new byte[5*1024*740]時觸發(fā)驶兜,然后發(fā)現(xiàn)內(nèi)存不夠,于是將softRef列入回收返回远寸,接著進(jìn)行了第三次full gc抄淑。
*/
// byte[] b = new byte[5*1024*740];
/**
* ====================== 控制臺打印 ======================
* After GC : Soft Get = I am MyObject.
* 分配大塊內(nèi)存
* Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
* at com.bayern.multi_thread.part5.SoftReferenceDemo.main(SoftReferenceDemo.java:21)
* MyObject's finalize called
* Object for softReference is null
* ====================== 控制臺打印 ======================
*
* 也是觸發(fā)了 3 次 full gc。第一次有System.gc();觸發(fā)驰后;第二次在在分配new byte[5*1024*740]時觸發(fā)肆资,然后發(fā)現(xiàn)內(nèi)存不夠,于是將softRef列入回收返回灶芝,接著進(jìn)行了第三次full gc郑原。當(dāng)?shù)谌?full gc 后發(fā)現(xiàn)內(nèi)存依舊不夠用于分配new byte[5*1024*740],則就拋出了OutOfMemoryError異常监署。
*/
byte[] b = new byte[5*1024*790];
System.out.println("After new byte[] : Soft Get = " + softRef.get());
}
public static class CheckRefQueue implements Runnable {
Reference<MyObject> obj = null;
@Override
public void run() {
try {
obj = (Reference<MyObject>) queue.remove();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (obj != null) {
System.out.println("Object for softReference is " + obj.get());
}
}
}
public static class MyObject {
@Override
protected void finalize() throws Throwable {
System.out.println("MyObject's finalize called");
super.finalize();
}
@Override
public String toString() {
return "I am MyObject.";
}
}
}
弱引用 ( Weak Reference )
用來描述非必須的對象颤专,但是它的強(qiáng)度比軟引用更弱一些纽哥,被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集發(fā)生之前钠乏。當(dāng)垃圾收集器工作時,無論當(dāng)前內(nèi)存是否足夠春塌,都會回收掉只被弱引用關(guān)聯(lián)的對象晓避。一旦一個弱引用對象被垃圾回收器回收,便會加入到一個注冊引用隊列中只壳。
注意:Java 垃圾回收器準(zhǔn)備對WeakReference所指向的對象進(jìn)行回收時俏拱,調(diào)用對象的 finalize() 方法之前,WeakReference對象自身會被加入到這個 ReferenceQueue 對象中吼句,此時可以通過 ReferenceQueue 的 poll() 方法取到它們锅必。
/**
* 用來描述非必須的對象,但是它的強(qiáng)度比軟引用更弱一些,被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集發(fā)送之前搞隐。當(dāng)垃圾收集器工作時驹愚,無論當(dāng)前內(nèi)存是否足夠,都會回收掉只被弱引用關(guān)聯(lián)的對象劣纲。一旦一個弱引用對象被垃圾回收器回收逢捺,便會加入到一個注冊引用隊列中。
*/
public class WeakReferenceDemo {
private static ReferenceQueue<MyObject> queue = new ReferenceQueue<>();
public static void main(String[] args) {
MyObject object = new MyObject();
Reference<MyObject> weakRef = new WeakReference<>(object, queue);
System.out.println("創(chuàng)建的弱引用為 : " + weakRef);
new Thread(new CheckRefQueue()).start();
object = null;
System.out.println("Before GC: Weak Get = " + weakRef.get());
System.gc();
System.out.println("After GC: Weak Get = " + weakRef.get());
/**
* ====================== 控制臺打印 ======================
* 創(chuàng)建的弱引用為 : java.lang.ref.WeakReference@1d44bcfa
* Before GC: Weak Get = I am MyObject
* After GC: Weak Get = null
* MyObject's finalize called
* 刪除的弱引用為 : java.lang.ref.WeakReference@1d44bcfa , 獲取到的弱引用的對象為 : null
* ====================== 控制臺打印 ======================
*/
}
public static class CheckRefQueue implements Runnable {
Reference<MyObject> obj = null;
@Override
public void run() {
try {
obj = (Reference<MyObject>)queue.remove();
} catch (InterruptedException e) {
e.printStackTrace();
}
if(obj != null) {
System.out.println("刪除的弱引用為 : " + obj + " , 獲取到的弱引用的對象為 : " + obj.get());
}
}
}
public static class MyObject {
@Override
protected void finalize() throws Throwable {
System.out.println("MyObject's finalize called");
super.finalize();
}
@Override
public String toString() {
return "I am MyObject";
}
}
}
虛引用 ( Phantom Reference )
PhantomReference 是所有“弱引用”中最弱的引用類型癞季。不同于軟引用和弱引用劫瞳,虛引用無法通過 get() 方法來取得目標(biāo)對象的強(qiáng)引用從而使用目標(biāo)對象,觀察源碼可以發(fā)現(xiàn) get() 被重寫為永遠(yuǎn)返回 null绷柒。
那虛引用到底有什么作用志于?其實(shí)虛引用主要被用來 跟蹤對象被垃圾回收的狀態(tài),通過查看引用隊列中是否包含對象所對應(yīng)的虛引用來判斷它是否 即將被垃圾回收辉巡,從而采取行動恨憎。它并不被期待用來取得目標(biāo)對象的引用,而目標(biāo)對象被回收前郊楣,它的引用會被放入一個 ReferenceQueue 對象中憔恳,從而達(dá)到跟蹤對象垃圾回收的作用。
當(dāng)phantomReference被放入隊列時净蚤,說明referent的finalize()方法已經(jīng)調(diào)用钥组,并且垃圾收集器準(zhǔn)備回收它的內(nèi)存了。
注意:PhantomReference 只有當(dāng) Java 垃圾回收器對其所指向的對象真正進(jìn)行回收時今瀑,會將其加入到這個 ReferenceQueue 對象中程梦,這樣就可以追綜對象的銷毀情況。這里referent對象的finalize()方法已經(jīng)調(diào)用過了橘荠。
所以具體用法和之前兩個有所不同屿附,它必須傳入一個 ReferenceQueue 對象。當(dāng)虛引用所引用對象準(zhǔn)備被垃圾回收時哥童,虛引用會被添加到這個隊列中挺份。
Demo1:
/**
* 虛引用也稱為幽靈引用或者幻影引用,它是最弱的一種引用關(guān)系贮懈。一個持有虛引用的對象匀泊,和沒有引用幾乎是一樣的,隨時都有可能被垃圾回收器回收朵你。
* 虛引用必須和引用隊列一起使用各聘,它的作用在于跟蹤垃圾回收過程。
* 當(dāng)phantomReference被放入隊列時抡医,說明referent的finalize()方法已經(jīng)調(diào)用躲因,并且垃圾收集器準(zhǔn)備回收它的內(nèi)存了。
*/
public class PhantomReferenceDemo {
private static ReferenceQueue<MyObject> queue = new ReferenceQueue<>();
public static void main(String[] args) throws InterruptedException {
MyObject object = new MyObject();
Reference<MyObject> phanRef = new PhantomReference<>(object, queue);
System.out.println("創(chuàng)建的虛擬引用為 : " + phanRef);
new Thread(new CheckRefQueue()).start();
object = null;
int i = 1;
while (true) {
System.out.println("第" + i++ + "次GC");
System.gc();
TimeUnit.SECONDS.sleep(1);
}
/**
* ====================== 控制臺打印 ======================
* 創(chuàng)建的虛擬引用為 : java.lang.ref.PhantomReference@1d44bcfa
* 第1次GC
* MyObject's finalize called
* 第2次GC
* 刪除的虛引用為: java.lang.ref.PhantomReference@1d44bcfa , 獲取虛引用的對象 : null
* ====================== 控制臺打印 ======================
*
* 再經(jīng)過一次GC之后,系統(tǒng)找到了垃圾對象大脉,并調(diào)用finalize()方法回收內(nèi)存搁嗓,但沒有立即加入PhantomReference Queue中。因為MyObject對象重寫了finalize()方法箱靴,并且該方法是一個非空實(shí)現(xiàn)腺逛,所以這里MyObject也是一個Final Reference。所以第一次GC完成的是Final Reference的事情衡怀。
* 第二次GC時棍矛,該對象(即,MyObject)對象會真正被垃圾回收器進(jìn)行回收抛杨,此時够委,將PhantomReference加入虛引用隊列( PhantomReference Queue )。
* 而且每次gc之間需要停頓一些時間怖现,已給JVM足夠的處理時間茁帽;如果這里沒有TimeUnit.SECONDS.sleep(1); 可能需要gc到第5、6次才會成功。
*/
}
public static class MyObject {
@Override
protected void finalize() throws Throwable {
System.out.println("MyObject's finalize called");
super.finalize();
}
@Override
public String toString() {
return "I am MyObject";
}
}
public static class CheckRefQueue implements Runnable {
Reference<MyObject> obj = null;
@Override
public void run() {
try {
obj = (Reference<MyObject>)queue.remove();
System.out.println("刪除的虛引用為: " + obj + " , 獲取虛引用的對象 : " + obj.get());
System.exit(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Q:??了解下System.gc()操作,如果連續(xù)調(diào)用卒暂,若前一次沒完成,后一次可能會失效铁追,所以連接調(diào)用System.gc()其實(shí)作用不大?
A:關(guān)于上面例子的問題我們要補(bǔ)充兩點(diǎn)
① 首先我們先來看下System.gc()的doc文檔:
/**
* Runs the garbage collector.
* <p>
* Calling the <code>gc</code> method suggests that the Java Virtual
* Machine expend effort toward recycling unused objects in order to
* make the memory they currently occupy available for quick reuse.
* When control returns from the method call, the Java Virtual
* Machine has made a best effort to reclaim space from all discarded
* objects.
* <p>
* The call <code>System.gc()</code> is effectively equivalent to the
* call:
* <blockquote><pre>
* Runtime.getRuntime().gc()
* </pre></blockquote>
*
* @see java.lang.Runtime#gc()
*/
public static void gc() {
Runtime.getRuntime().gc();
}
當(dāng)這個方法返回的時候茫船,Java虛擬機(jī)已經(jīng)盡最大努力去回收所有丟棄對象的空間了琅束。
因此不存在這System.gc()操作連續(xù)調(diào)用時,若前一次沒完成算谈,后一次可能會失效的情況涩禀。以及“所以連接調(diào)用System.gc()其實(shí)作用不大”這個說法不對,應(yīng)該說連續(xù)調(diào)用System.gc()對性能可定是有影響的然眼,但作用之一就是可以清除“漂浮垃圾”艾船。
② 同時需要特別注意的是對于已經(jīng)沒有地方引用的這些f對象,并不會在最近的那一次gc里馬上回收掉罪治,而是會延遲到下一個或者下幾個gc時才被回收丽声,因為執(zhí)行finalize方法的動作無法在gc過程中執(zhí)行礁蔗,萬一finalize方法執(zhí)行很長呢觉义,所以只能在這個gc周期里將這個垃圾對象重新標(biāo)活,直到執(zhí)行完finalize方法將Final Reference從queue里刪除浴井,這樣下次gc的時候就真的是漂浮垃圾了會被回收晒骇。
Demo2:
public class PhantomReferenceDemo2 {
public static void main(String[] args) {
ReferenceQueue<MyObject> queue = new ReferenceQueue<>();
MyObject object = new MyObject();
Reference<MyObject> phanRef = new PhantomReference<>(object, queue);
System.out.println("創(chuàng)建的虛擬引用為 : " + phanRef);
object = null;
System.out.println(phanRef.get());
System.gc();
System.out.println("referent : " + phanRef);
System.out.println(queue.poll() == phanRef); //true
/**
* ====================== 控制臺打印 ======================
* 創(chuàng)建的虛擬引用為 : java.lang.ref.PhantomReference@1d44bcfa
* null
* referent : java.lang.ref.PhantomReference@1d44bcfa
* true
* ====================== 控制臺打印 ======================
*
* 這里因為MyObject沒有重寫finalize()方法,所以這里的在System.gc()后就會處理PhantomReference加入到PhantomReference Queue中。
*/
}
public static class MyObject {
@Override
public String toString() {
return "I am MyObject";
}
}
}