JDK1.2之后,java對(duì)引用的概念進(jìn)行了拓充,將引用分為強(qiáng)引用,軟引用,弱引用,虛引用
- 強(qiáng)引用: 指的是在代碼之中普遍存在的,類似
Object obj = new Object()
這類的引用,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象 - 軟引用: 用來(lái)描述一些還有用,但是并非重要的對(duì)象.對(duì)于軟引用關(guān)聯(lián)著的對(duì)象,在系統(tǒng)將要發(fā)生內(nèi)存溢出之前,將會(huì)把這些對(duì)象列進(jìn)回收范圍之中并進(jìn)行第二次回收.如果這次回收還是沒(méi)有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常.
- 弱飲用: 當(dāng)垃圾收集器工作時(shí),無(wú)論是否內(nèi)存足夠,都將回收掉只被若飲用關(guān)聯(lián)的對(duì)象
- 虛引用: 一個(gè)對(duì)象是否是有虛引用的存在,完全不會(huì)對(duì)其生成時(shí)間構(gòu)成影響,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例.為一個(gè)對(duì)象設(shè)置虛引用關(guān)聯(lián)的唯一目的是希望在其被收集器回收時(shí)收到一個(gè)系統(tǒng)通知.
強(qiáng)引用
JVM在GC的時(shí)候并不會(huì)釋放強(qiáng)引用的堆實(shí)例, 因此當(dāng)堆內(nèi)GC后仍然不能獲得足夠的空間, 就會(huì)發(fā)生OOM
String str = new String("Hi");
上面的例子中, 在棧中分配的str
指向了堆中分配的String實(shí)例, 那么str
引用就是這個(gè)實(shí)例的強(qiáng)引用.
保存在數(shù)組和集合中以及Map中的引用都都算是強(qiáng)引用.
軟引用
JVM在GC時(shí)不一定會(huì)釋放軟引用所引用的對(duì)象實(shí)例, 那什么時(shí)候會(huì)進(jìn)行釋放呢? 只有當(dāng)JVM發(fā)現(xiàn)堆內(nèi)存不足時(shí), 才會(huì)在GC時(shí)將軟引用的堆內(nèi)存釋放掉
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
public class TestSoft {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<User> referenceQueue = new ReferenceQueue<>();
User user = new User();
SoftReference<User> softReference = new SoftReference<>(user, referenceQueue);
user = null;
Thread t = new Thread(() -> {
while (true) {
Reference<? extends User> ref = referenceQueue.poll();
if (ref != null) {
System.out.println("Changed : " + ref);
break;
}
}
});
t.setDaemon(true);
t.start();
System.out.println("Before GC : " + " " + softReference.get());
System.gc();
System.out.println("After GC : " + softReference.get());
byte[] array = new byte[1024 * 920 * 7];
System.out.println("Alocate : " + softReference.get());
}
}
class User {
public String name;
}
我們指定虛擬機(jī)參數(shù)-Xmx10M -Xms10M -XX:PrintGC
, 運(yùn)行一下這個(gè)程序的結(jié)果為:
[GC (Allocation Failure) 2048K->836K(9728K), 0.0023890 secs]
Before GC : testRef.User@404b9385
[GC (System.gc()) 1145K->844K(9728K), 0.0013400 secs]
[Full GC (System.gc()) 844K->750K(9728K), 0.0085260 secs]
After GC : testRef.User@404b9385
[GC (Allocation Failure) 788K->782K(9728K), 0.0003760 secs]
[GC (Allocation Failure) 782K->782K(9728K), 0.0002590 secs]
[Full GC (Allocation Failure) 782K->750K(9728K), 0.0043290 secs]
[GC (Allocation Failure) 750K->750K(9728K), 0.0004580 secs]
[Full GC (Allocation Failure) 750K->692K(9728K), 0.0079430 secs]
Changed : java.lang.ref.SoftReference@19366529
Alocate : null
我們?cè)跇?gòu)建SoftReference
實(shí)例對(duì)象時(shí), 除了添加一個(gè)測(cè)試對(duì)象外, 還添加里一個(gè)ReferenceQueue
實(shí)例對(duì)象, 當(dāng)對(duì)象的可達(dá)狀態(tài)發(fā)生改變時(shí), SoftReference
就會(huì)移動(dòng)到ReferenceQueue
隊(duì)列里. 從最后的Poll 這個(gè)輸出里我們可以看到, 已經(jīng)看不到這個(gè)對(duì)象了.
弱引用
弱引用是一種比軟飲用更加弱的引用, JVM在GC時(shí)只要發(fā)現(xiàn)弱引用, 都會(huì)對(duì)其引用的實(shí)例進(jìn)行回收
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
public class TestSoft {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<User> referenceQueue = new ReferenceQueue<>();
User user = new User();
WeakReference<User> softReference = new WeakReference<>(user, referenceQueue);
// 確定沒(méi)有強(qiáng)引用
user = null;
Thread t = new Thread(() -> {
while (true) {
Reference<? extends User> ref = referenceQueue.poll();
if (ref != null) {
System.out.println("Changed : " + ref);
break;
}
}
});
t.setDaemon(true);
t.start();
System.out.println("Before GC : " + " " + softReference.get());
System.gc();
System.out.println("After GC : " + softReference.get());
byte[] array = new byte[1024 * 920 * 7];
System.out.println("Alocate : " + softReference.get());
}
}
class User {}
我們指定虛擬機(jī)參數(shù)-Xmx10M -Xms10M -XX:+PrintGC
, 運(yùn)行一下這個(gè)程序的結(jié)果為:
[GC (Allocation Failure) 2048K->800K(9728K), 0.0031060 secs]
Before GC : null
Changed : java.lang.ref.WeakReference@175fdc70[GC (System.gc()) 1084K->824K(9728K), 0.0011480 secs]
[Full GC (System.gc()) 824K->748K(9728K), 0.0088060 secs]
After GC : null
[GC (Allocation Failure) 807K->812K(9728K), 0.0010100 secs]
[GC (Allocation Failure) 812K->844K(9728K), 0.0004150 secs]
[Full GC (Allocation Failure) 844K->748K(9728K), 0.0090930 secs]
[GC (Allocation Failure) 748K->748K(9728K), 0.0003230 secs]
[Full GC (Allocation Failure) 748K->690K(9728K), 0.0082600 secs]
Alocate : null
如果WeakReference
是保存在一個(gè)對(duì)象實(shí)例里面是什么情況呢?
import java.lang.ref.WeakReference;
public class TestSoft {
public static void main(String[] args) throws InterruptedException {
WeakReferenceCache weakReferenceCache = new WeakReferenceCache();
weakReferenceCache.cache = new WeakReference<>(new User());
System.out.println("Before GC : " + weakReferenceCache.cache.get());
System.gc();
System.out.println("After GC : " + weakReferenceCache.cache.get());
byte[] array = new byte[1024 * 920 * 7];
System.out.println("Alocate GC : " + weakReferenceCache.cache.get());
}
}
class WeakReferenceCache {
public WeakReference<User> cache;
}
class User {
}
同樣我們運(yùn)行一下看一下結(jié)果
Before GC : User@41629346
After GC : null
Alocate GC : null
確實(shí)是, 每次GC都將其回收掉了
我們?cè)賹?shí)驗(yàn)一下, 如果將其存進(jìn)一個(gè)列表里
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
public class TestSoft {
public static void main(String[] args) throws InterruptedException {
WeakReferenceCache weakReferenceCache = new WeakReferenceCache();
for (int i = 0; i < 10; i++) {
WeakReference<User> softReference = new WeakReference<>(new User());
weakReferenceCache.cache.add(softReference);
}
System.out.println("Before GC : ");
weakReferenceCache.cache.forEach(cache -> {
System.out.println(cache.get());
});
System.gc();
System.out.println("After GC : ");
weakReferenceCache.cache.forEach(cache -> {
System.out.println(cache.get());
});
byte[] array = new byte[1024 * 920 * 7];
System.out.println("Alocate GC : ");
weakReferenceCache.cache.forEach(cache -> {
System.out.println(cache.get());
});
}
}
class WeakReferenceCache {
public List<WeakReference<User>> cache = new ArrayList<>();
}
class User {
}
結(jié)果為
Before GC :
User@6433a2
User@5910e440
User@6267c3bb
User@533ddba
User@246b179d
User@7a07c5b4
User@26a1ab54
User@3d646c37
User@41cf53f9
User@5a10411
After GC :
null
null
null
null
null
null
null
null
null
null
Alocate GC :
null
null
null
null
null
null
null
null
null
null
即使是存儲(chǔ)在數(shù)組里也一樣被回收掉了
虛引用
虛引用是所有引用類型中最弱的一個(gè), 一個(gè)被虛引用持有的對(duì)象跟沒(méi)有被持有的效果基本上是一樣的. 當(dāng)我們從虛引用中g(shù)et時(shí), 總會(huì)獲得一個(gè)空, 那既然如此還為什么要設(shè)計(jì)出一個(gè)這樣的引用呢? 因?yàn)樘撘帽仨毟粋€(gè)引用隊(duì)列, 我們可以將一些資源性的東西放到虛引用中執(zhí)行和記錄.
import java.lang.ref.*;
public class TestSoft {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<User> referenceQueue = new ReferenceQueue<>();
User user = new User();
PhantomReference<User> softReference = new PhantomReference<>(user, referenceQueue);
user = null;
Thread t = new Thread(() -> {
while (true) {
Reference<? extends User> ref = referenceQueue.poll();
if (ref != null) {
System.out.println("Changed : " + System.currentTimeMillis());
break;
}
}
});
t.setDaemon(true);
t.start();
System.out.println("Before GC : " + System.currentTimeMillis() + " " + softReference.get());
System.gc();
System.out.println("After GC : " + softReference.get());
byte[] array = new byte[1024 * 920 * 7];
System.out.println("Alocate : " + softReference.get());
}
}
class User {}
我們指定虛擬機(jī)參數(shù)-Xmx30M -Xms30M -XX:+PrintGC
, 運(yùn)行一下這個(gè)程序的結(jié)果為:
Before GC : 1462461362835 null
[GC (System.gc()) 2806K->904K(29696K), 0.0033390 secs]
[Full GC (System.gc()) 904K->779K(29696K), 0.0095950 secs]
Changed : 1462461362850
After GC : null
Alocate : null