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)

 

posted @ 2020-09-15 15:08  阿呆很呆非常呆  阅读(214)  评论(0编辑  收藏  举报