java对象引用-强弱软虚
一、简述
Java对象的四种引用:强软弱虚。通过四种引用的使用,开发者通过代码去决定对象的生命周期,从而有利于JVM的垃圾回收。在各种面试环节中被问及的频率也是相当高的,比如对于弱引用,一般结合到ThreadLocal。除了强引用,其他几种引用都是借助于引用类去完成的。
二、强引用
基本上,开发者都是使用的格式,如 Object obj = new Object(); 就是个强引用。一般情况下,只要某个对象有强引用与之关联的,JVM就不会去回收这玩意。即使内存溢出OutOfMemoryError,也不会强制的去回收,只会程序异常终止。
当然了,如果显式置obj=null ,那么JVM会适时的去回收掉改对象。光说不练假把式,上实验对象:
1 public class ReferenceTest { 2 3 public static void main(String[] args) { 4 ReferenceTest referenceTest = new ReferenceTest(); 5 referenceTest = null; 6 System.gc(); 7 } 8 9 @Override 10 protected void finalize() throws Throwable { 11 super.finalize(); 12 System.out.println("回收 ReferenceTest 对象..."); 13 } 14 }
实验对象中,重写了 finalize 方法,在对象置为 null 并 手动提醒 GC的时候,打印出结果 “回收 ReferenceTest 对象...”,也就说明资源被回收。当然实际开发中,一般不会去重写 finalize 方法,并且手动的置 null 操作,也是提醒JVM去适时的可回收这部分对象。
JDK中,如HashMap中的 clear 方法
public void clear() { modCount++; Arrays.fill(table, null); size = 0; } public static void fill(Object[] a, Object val) { for (int i = 0, len = a.length; i < len; i++) a[i] = val; }
三、软引用
类:SoftReference
内存不足,触发 GC ,回收完成还不足,再干掉 SoftReference 中包装的对象,也就是说对象的软引用,会根据内存状态来决定是否回收,内存充足,gc即使扫到,也不会处理。相反,内存不足了,那么触发gc,就会回收对象的内存。
实验对象走一遍
public class ReferenceTest { public static void main(String[] args) { SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024 * 1024 * 10]); //10M的byte数组 System.out.println(softReference.get()); System.gc(); System.out.println(softReference.get()); //内存充足,不会被回收,取得到对象 byte[] bytes = new byte[1024 * 1024 * 10]; System.out.println(softReference.get()); } }
程序运行时,设置 JVM 最大堆内存参数 : -Xmx20M ,初始时 SoftReference 包装一个 10M 空间大小的字节数组,后续再申请一个10M大小的空间,内存不足,触发GC,回收掉软引用对象,不然就OOM了。结果如下:
[B@1b6d3586 [B@1b6d3586 null
四、弱引用
类:WeakReference
生命周期很短,理论上两次gc的时间间隔就是存活时间。如果包裹的对象没有外部强引用的时候,不管内存情况,直接回收掉。
public class ReferenceTest {
public static void main(String[] args) throws Exception{
ReferenceTest referenceTest = new ReferenceTest();
WeakReference<ReferenceTest> weakReference = new WeakReference<>(referenceTest);
System.out.println(weakReference.get());
System.gc();
System.out.println(weakReference.get());
referenceTest = null;
System.gc();
System.out.println(weakReference.get());
}
}
运行结果:
com.cfang.reference.ReferenceTest@12a3a380
com.cfang.reference.ReferenceTest@12a3a380
null
可以看出,内存充足情况下,触发GC的时候,对象资源还是被回收掉了。常见的JDK中使用: ThreadLocal。
五、虚引用
类:PhantomReference
幽灵引用,一般结合着 ReferenceQueue 使用,在GC的时候,对象资源被回收并且此对象的虚引用被放到队列中。上实验对象:
public class ReferenceTest { public static void main(String[] args) throws Exception{ ReferenceQueue queue = new ReferenceQueue(); PhantomReference<ReferenceTest> phantomReference = new PhantomReference<>(new ReferenceTest(), queue); List<byte[]> list = Lists.newArrayList(); new Thread(() -> { for (int i = 0; i < 100; i++){ System.out.println("第" + i + "次放数据"); list.add(new byte[1024 * 1024 * 1]); } }).start(); new Thread(() -> { while (true){ Reference reference = queue.poll(); if(null != reference){ System.out.println("虚应用被回收..." + reference); } } }).start(); Thread.currentThread().join(); } @Override protected void finalize() throws Throwable { System.out.println("回收 ReferenceTest 对象..."); } }
实验中,第一个线程去申请内存空间并加到集合中,随着次数的累计,必然发生GC。第二个线程中,循环去获取队列数据。从最终运行结果上看,发生GC的时候,虚引用对象被回收掉,并且虚引用被放到队列中去。
第0次放数据 第1次放数据 第2次放数据 回收 ReferenceTest 对象... 第3次放数据 虚应用被回收...java.lang.ref.PhantomReference@5c4497a5 Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space at com.cfang.reference.ReferenceTest.lambda$main$0(ReferenceTest.java:23) at com.cfang.reference.ReferenceTest$$Lambda$1/363771819.run(Unknown Source) at java.lang.Thread.run(Thread.java:748)