一、 概念。
强引用 -> Object strong = new Object(); ,类似这种代码,都是强引用,即使OutOfMemory,也不会被回收。通过设置strong = null,可以去除强引用。
软引用 -> SoftReference,在OutOfMemory时被回收。用来描述一些还有用但是并非必须的对象。可以和一个引用队列(ReferenceQueue)联合使用,如果所引用的对象被垃圾回收器回收,Java虚拟机就会把这个引用加入到与之关联的引用队列中。
弱引用->WeakReference,在垃圾回收器工作时,无论内存是否足够,都会被回收。可以和一个引用队列(ReferenceQueue)联合使用,如果所引用的对象被垃圾回收器回收,Java虚拟机就会把这个引用加入到与之关联的引用队列中。
虚引用->PhantomReference,也称为幻影引用,一个对象是都有虚引用的存在都不会对其生存时间构成影响,也无法通过虚引来取得一个对象实例。唯一的用处:能在对象被回收时收到系统通知。
二、简单测试强引用、软引用和弱引用。
测试代码:
private static final int _1M = 1024 * 1024;
private static void useprotosomaticAIP() {
/* 分配1M,目前新生代空间使用1M */
printPrepareMessage("strong1");
LargeObject strong1 = new LargeObject(_1M, "strong1");
/* 分配4M,目前新生代空间使用5M */
SoftReference<LargeObject> soft1 = new SoftReference<LargeObject>(
new LargeObject(2 * _1M, "soft1"));
SoftReference<LargeObject> soft2 = new SoftReference<LargeObject>(
new LargeObject(2 * _1M, "soft2"));
/* 分配6M,目前新生代空间使用11M */
WeakReference<LargeObject> weak1 = new WeakReference<LargeObject>(
new LargeObject(3 * _1M, "weak1"));
WeakReference<LargeObject> weak2 = new WeakReference<LargeObject>(
new LargeObject(3 * _1M, "weak2"));
/* 分配30M,触发GC和OutOfMemory */
printPrepareMessage("strong2");
LargeObject strong2 = new LargeObject(30 * _1M, "strong2");
}
static class LargeObject {
private byte[] data;
private String name;
public LargeObject(int size, String name) {
data = new byte[size];
this.name = name;
System.out.println("Over Constructing LargeObject " + name + System.lineSeparator());
}
public String getName() {
return name;
}
}
测试环境:JDK1.8.0_144,Java HotSpot(TM) 64-Bit Server VM
-Xms40m -Xmx40m -Xmn20m -XX:+PrintGCDetails
测试结果及分析:
Prepare to create strong1
Over Constructing LargeObject strong1
Over Constructing LargeObject soft1
Over Constructing LargeObject soft2
Over Constructing LargeObject weak1
Over Constructing LargeObject weak2
Prepare to create strong2
// 需要30M空间,内存不足,第一次MinorGC,回收弱引用的6M空间
[GC (Allocation Failure) [PSYoungGen: 12493K->2536K(17920K)] 12493K->5760K(38400K), 0.0026251 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
// 内存依旧不够,第二次MinorGC,无对象可回收
[GC (Allocation Failure) [PSYoungGen: 2536K->2504K(17920K)] 5760K->5728K(38400K), 0.0016023 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
// 内存依旧不够,第一次FullGC,eden space和from space的内存全部移入老年代
[Full GC (Allocation Failure) [PSYoungGen: 2504K->0K(17920K)] [ParOldGen: 3224K->5674K(20480K)] 5728K->5674K(38400K), [Metaspace: 2782K->2782K(1056768K)], 0.0061579 secs] [Times: user=0.00 sys=0.05, real=0.01 secs]
// 内存依旧不够,第三次MinorGC,无对象可回收
[GC (Allocation Failure) [PSYoungGen: 0K->0K(17920K)] 5674K->5674K(38400K), 0.0002198 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
// 内存依旧不够,即将OutOfMemory,第二次FullGC,回收软引用的4M空间
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(17920K)] [ParOldGen: 5674K->1566K(20480K)] 5674K->1566K(38400K), [Metaspace: 2782K->2782K(1056768K)], 0.0049224 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
// 内存不足,打印异常
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.test.reference.TestReference$LargeObject.<init>(TestReference.java:84)
at com.test.reference.TestReference.useprotosomaticAIP(TestReference.java:38)
at com.test.reference.TestReference.main(TestReference.java:15)
Heap
PSYoungGen total 17920K, used 453K [0x00000000fec00000, 0x0000000100000000, 0x0000000100000000)
eden space 15360K, 2% used [0x00000000fec00000,0x00000000fec71678,0x00000000ffb00000)
from space 2560K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x00000000ffd80000)
to space 2560K, 0% used [0x00000000ffd80000,0x00000000ffd80000,0x0000000100000000)
ParOldGen total 20480K, used 1566K [0x00000000fd800000, 0x00000000fec00000, 0x00000000fec00000)
object space 20480K, 7% used [0x00000000fd800000,0x00000000fd9879e8,0x00000000fec00000)
Metaspace used 2812K, capacity 4490K, committed 4864K, reserved 1056768K
class space used 304K, capacity 386K, committed 512K, reserved 1048576K
三、如果弱引用或者虚引用的对象又有强引用,则不会被清除,继续上面的例子,修改测试方法,环境不变。
测试代码:
private static void referenceIsStrongUseProtosomaticAIP() {
/* 分配1M,目前新生代空间使用1M */
printPrepareMessage("strong1");
LargeObject strong1 = new LargeObject(_1M, "strong1");
LargeObject softObj1 = new LargeObject(2 * _1M, "softObj1");
LargeObject softObj2 = new LargeObject(2 * _1M, "softObj2");
/* 分配4M,目前新生代空间使用5M */
SoftReference<LargeObject> soft1 = new SoftReference<LargeObject>(softObj1);
SoftReference<LargeObject> soft2 = new SoftReference<LargeObject>(softObj2);
LargeObject weakObj1 = new LargeObject(3 * _1M, "weakObj1");
LargeObject weakObj2 = new LargeObject(3 * _1M, "weakObj2");
/* 分配6M,目前新生代空间使用11M */
WeakReference<LargeObject> weak1 = new WeakReference<LargeObject>(weakObj2);
WeakReference<LargeObject> weak2 = new WeakReference<LargeObject>(weakObj2);
/* eden区为16M,申请30M,触发MinorGC */
printPrepareMessage("strong2");
LargeObject strong2 = new LargeObject(30 * _1M, "strong2");
}
测试环境:JDK1.8.0_144,Java HotSpot(TM) 64-Bit Server VM
-Xms40m -Xmx40m -Xmn20m -XX:+PrintGCDetails
测试结果及分析:
Prepare to create strong1
Over Constructing LargeObject strong1
Over Constructing LargeObject softObj1
Over Constructing LargeObject softObj2
Over Constructing LargeObject weakObj1
Over Constructing LargeObject weakObj2
Prepare to create strong2
// 无内存可回收,因为弱引用的对象被强引用了
[GC (Allocation Failure) [PSYoungGen: 12493K->1704K(17920K)] 12493K->11952K(38400K), 0.0058929 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
// 无内存可回收,因为弱引用的对象被强引用了
[Full GC (Ergonomics) [PSYoungGen: 1704K->0K(17920K)] [ParOldGen: 10248K->11818K(20480K)] 11952K->11818K(38400K), [Metaspace: 2783K->2783K(1056768K)], 0.0095241 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
// 无内存可回收,因为弱引用的对象被强引用了
[GC (Allocation Failure) [PSYoungGen: 0K->0K(17920K)] 11818K->11818K(38400K), 0.0003058 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
// 即将OOM,无内存可回收,因为软引用的对象被强引用了
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(17920K)] [ParOldGen: 11818K->11806K(20480K)] 11818K->11806K(38400K), [Metaspace: 2783K->2783K(1056768K)], 0.0059819 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.test.reference.TestReference$LargeObject.<init>(TestReference.java:170)
at com.test.reference.TestReference.referenceIsStrongUseProtosomaticAIP(TestReference.java:66)
at com.test.reference.TestReference.main(TestReference.java:22)
Heap
PSYoungGen total 17920K, used 453K [0x00000000fec00000, 0x0000000100000000, 0x0000000100000000)
eden space 15360K, 2% used [0x00000000fec00000,0x00000000fec71678,0x00000000ffb00000)
from space 2560K, 0% used [0x00000000ffd80000,0x00000000ffd80000,0x0000000100000000)
to space 2560K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x00000000ffd80000)
ParOldGen total 20480K, used 11806K [0x00000000fd800000, 0x00000000fec00000, 0x00000000fec00000)
object space 20480K, 57% used [0x00000000fd800000,0x00000000fe387850,0x00000000fec00000)
Metaspace used 2813K, capacity 4494K, committed 4864K, reserved 1056768K
class space used 303K, capacity 386K, committed 512K, reserved 1048576K
四、常量不会被回收
测试代码:
private static void useConstantVariable() {
WeakReference<String> weak1 = new WeakReference<String>("constant1");
WeakReference<String> weak2 = new WeakReference<String>(new String("constant2"));
System.gc();
System.out.println(weak1.get());
System.out.println(weak2.get());
}
测试环境:JDK1.8.0_144,Java HotSpot(TM) 64-Bit Server VM
测试结果及分析:
constant1 // "constant1"存在常量池,GC主要针对堆
null // "constant2"是在堆里面,所以会被GC