慢慢学JVM之强引用?软引用?弱引用?虚引用?
从JDK1.2版本开始,引用被划分为4种级别,使程序能更加灵活地控制对象的生命周期。4种级别由强到弱依次为:强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)。
强引用
强引用最普遍,正常创建一个对象就是强引用。如果一个对象的引用是强引用,那么垃圾回收器绝不会回收它,注意绝对不会回收,即使会发生OutOfMemoryError。
Object obj = new Object();
后续软引用、弱引用、虚引用的创建都需要有一个前提,就是存在一个强引用的对象。
软引用
软引用比强引用弱一点,当内存空间充足的时候,它不会被回收,当内存空间不足的时候,它才会被回收。软引用通过SoftReference类实现的。根据软引用的特性,软引用适合作为缓存数据的引用级别。
关于软引用回收时机的解析:https://www.jianshu.com/p/e46158238a77
示例代码
/**
* 运行参数: -Xmx10m -XX:+PrintGC
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
BClass a = new BClass();
a.setCString("a String");
SoftReference<BClass> stringSoftReference = new SoftReference<>(a);
System.out.println("初始化阶段-String:" + a);
System.out.println("初始化阶段-SoftReference:" + stringSoftReference.get());
// 去除强引用
a = null;
System.out.println("去除强引用阶段-SoftReference:" + stringSoftReference.get());
// 执行FullGC
System.gc();
System.out.println("执行FullGC阶段-SoftReference:" + stringSoftReference.get());
//缓存数据
byte[] cacheData = new byte[7 * 942 * 1024]; // 6594 * 1024
System.gc();
System.out.println("缓存数据阶段-SoftReference:" + stringSoftReference.get());
}
}
结果-如果删除a = null,便不会被回收,因为对象还在被强引用着。
初始化阶段-String:BClass(cString=a String)
初始化阶段-SoftReference:BClass(cString=a String)
去除强引用阶段-SoftReference:BClass(cString=a String)
[GC (System.gc()) 870K->664K(9728K), 0.0017426 secs]
[Full GC (System.gc()) 664K->581K(9728K), 0.0089447 secs]
执行FullGC阶段-SoftReference:BClass(cString=a String)
[GC (Allocation Failure) 622K->613K(9728K), 0.0020401 secs]
[GC (Allocation Failure) 613K->613K(9728K), 0.0009009 secs]
[Full GC (Allocation Failure) 613K->580K(8192K), 0.0078276 secs]
[GC (Allocation Failure) 580K->580K(9728K), 0.0005522 secs]
[Full GC (Allocation Failure) 580K->568K(9728K), 0.0103109 secs]
[GC (System.gc()) 7162K->7162K(9728K), 0.0009360 secs]
[Full GC (System.gc()) 7162K->7162K(9728K), 0.0043472 secs]
缓存数据阶段-SoftReference:null
这里有个小细节。当我们需要对字符串做软引用的时候,如果我们使用
String a = "a String";
并不会被回收,而是需要使用new String才能被回收。
String a = new String("a String");
弱引用
弱引用比软引用弱一点,遇到GC就会被回收。弱引用像一个临时工,适用于那种临时工数据,在公司有难时,需要第一个做出牺牲的那种。
示例代码
/**
* 运行参数: -Xmx10m -XX:+PrintGC
*/
public class Test {
public static void main(String[] args) {
String a = new String("a String");
WeakReference<String> weakReference = new WeakReference<>(a);
System.out.println("初始化阶段-String:" + a);
System.out.println("初始化阶段-WeakReference:" + weakReference.get());
// 去除强引用
a = null;
System.out.println("去除强引用阶段-WeakReference:" + weakReference.get());
// 执行FullGC
System.gc();
System.out.println("执行FullGC阶段-WeakReference:" + weakReference.get());
}
}
结果
初始化阶段-String:a String
初始化阶段-WeakReference:a String
去除强引用阶段-WeakReference:a String
[GC (System.gc()) 870K->668K(9728K), 0.0010758 secs]
[Full GC (System.gc()) 668K->580K(9728K), 0.0062521 secs]
执行FullGC阶段-WeakReference:null
虚引用
虚引用跟软引用和弱引用不同,它不会影响对象的生命周期。如果一个对象仅被虚引用,相当于没有任何引用,随时可以被回收。虚引用必须要与引用队列(ReferenceQueue)一起使用。
虚引用一般用来管理堆外内存。
示例代码
public class Test {
public static void main(String[] args) {
BClass a = new BClass();
a.setCString("a String");
ReferenceQueue<BClass> referenceQueue = new ReferenceQueue<>();
PhantomReference<BClass> phantomReference = new PhantomReference<>(a, referenceQueue);
System.out.println("初始化阶段-String:" + a);
System.out.println("初始化阶段-PhantomReference:" + phantomReference.get());
// 去除强引用
a = null;
System.out.println("去除强引用阶段-PhantomReference1:" + phantomReference.get());
// 执行FullGC
System.gc();
System.out.println("执行FullGC阶段-PhantomReference:" + phantomReference.get());
Reference<?> reference = referenceQueue.poll();
if (reference != null) {
System.out.println("被回收对象:" + reference.get());
}
}
}
结果
初始化阶段-String:BClass(cString=a String)
初始化阶段-PhantomReference:null
去除强引用阶段-PhantomReference1:null
[GC (System.gc()) 2600K->768K(249344K), 0.0011438 secs]
[Full GC (System.gc()) 768K->581K(249344K), 0.0056454 secs]
执行FullGC阶段-PhantomReference:null
被回收对象:null
引用队列
引用队列可以配合软引用、弱引用和虚引用使用。引用队列的的作用是收集被JVM回收的引用对象。
示例代码
public class Test {
public static void main(String[] args) {
// 创建引用队列
ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
// 软引用
SoftReference<String> softReference = new SoftReference<>(new String("softReferenceString"), referenceQueue);
// 弱引用
WeakReference<String> weakReference = new WeakReference<>(new String("weekReferenceString"), referenceQueue);
// 虚引用
PhantomReference<String> phantomReference = new PhantomReference<>(new String("phantomReferenceString"), referenceQueue);
System.out.println("GC前SoftReference:" + softReference.get());
System.out.println("GC前WeakReference:" + weakReference.get());
System.out.println("GC前PhantomReference:" + phantomReference.get());
System.gc();
System.out.println("GC后SoftReference:" + softReference.get());
System.out.println("GC后WeakReference:" + weakReference.get());
System.out.println("GC后PhantomReference:" + phantomReference.get());
Reference<?> reference = referenceQueue.poll();
while (reference != null) {
System.out.println("被回收引用:" + reference.get());
reference = referenceQueue.poll();
}
}
}
结果
GC前SoftReference:softReferenceString
GC前WeakReference:weekReferenceString
GC前PhantomReference:null
[GC (System.gc()) 2600K->736K(249344K), 0.0011499 secs]
[Full GC (System.gc()) 736K->581K(249344K), 0.0048663 secs]
GC后SoftReference:softReferenceString
GC后WeakReference:null
GC后PhantomReference:null
被回收引用:null
被回收引用:null
当引用关联对象实现了finalize方法的话,引用队列是不起作用的。
上一篇: QT导出Excel数据
下一篇: 【openjudge】魔兽世界(终极版)