JVM垃圾回收-强引用, 软引用, 弱引用, 虚引用以及终结器引用(三)

概述

  • 对象的引用级别为4种, 由高到低依次分别为: 强引用, 软引用, 弱引用和虚引用

强引用(String Reference)

  • 使用 new操作符创建的对象, 并将其赋值给一个变量的时候, 此变量就成了指向该对象的一个强引用. 也就是默认的引用方式
  • 当内存空间不足时, JVM宁愿抛出 OOM异常, 也不会靠释放具有强引用的对象来解决内存不足的问题
  • 实例:

Object obj = new Object();

软引用(Soft Reference)

  • 软引用对象是在 JVM内存不足时才会被回收(如果被强引用关联着就不会被回收)
  • 软引用通常实现缓存相关的 如高速缓存(当有空闲内存, 就会保留缓存, 否则清理掉)就是用到软引用, 还有 mybatis内部类也有使用软引用
  • 实例:

/**
 * 设置堆空间大小: -Xms3m -Xmx3m -XX:+PrintGCDetails
 * */
public class SoftReferenceApp {
    public static void main(String[] args) throws Exception {
        /** 创建 `objA`强引用变量*/
        byte[] objA = new byte[1024 * 1024];
        /** 创建软引用变量*/
        SoftReference<byte[]> reference = new SoftReference<>(objA);
        if (reference.get() == null) {
            System.out.println("reference is null");
        } else {
            System.out.println("reference is " + reference.get());
        }
        /** 将强引用删除(为了与`软引用对象 reference`的联系给失效), 然后调用垃圾回收*/
        objA = null;
        //System.gc();
        /** 由于 Finalizer线程优先级较低, 暂停2秒, 为了保证 GC的执行*/
        //Thread.sleep(2000);
        /** 创建 `objB`强引用变量
         * - 创建以下数据的同时, 由于老年代空间不足, 会触发 Full GC, 但是容量依然不够, 所以会将软引用删除, 以此维持此次的执行进程
         * */
        byte[] objB = new byte[1024 * 2024];

        System.out.println("reference is " + reference.get());
    }
}

输出:
[GC (Allocation Failure) [PSYoungGen: 512K->384K(1024K)] 512K->384K(3584K), 0.0012481 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 892K->464K(1024K)] 892K->464K(3584K), 0.0016060 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 976K->496K(1024K)] 976K->504K(3584K), 0.0011251 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
reference is [B@610455d6
[GC (Allocation Failure) [PSYoungGen: 675K->496K(1024K)] 1707K->1544K(3584K), 0.0009048 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 496K->448K(1024K)] 1544K->1496K(3584K), 0.0007774 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 448K->0K(1024K)] [ParOldGen: 1048K->1438K(2560K)] 1496K->1438K(3584K), [Metaspace: 3285K->3285K(1056768K)], 0.0070078 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1024K)] 1438K->1438K(3584K), 0.0004453 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1024K)] [ParOldGen: 1438K->397K(2560K)] 1438K->397K(3584K), [Metaspace: 3285K->3285K(1056768K)], 0.0060445 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
reference is null # 软引用被释放因此输出 null

  • 当执行 Major GC/Full GC后发现内存还是不足时, 会尝试第二次回收, 这一次会将软引用对象回收掉. 最后如果依然不足就会抛异常

弱引用(Weak Reference)

  • 弱引用对象只能生存到下一次垃圾回收之前, 也就是无论内存空间是否足够都会回收(如果被强引用关联着就不会被回收)
  • 弱引用也用与缓存
  • 实例:

/**
 * 设置堆空间大小: -Xms3m -Xmx3m -XX:+PrintGCDetails
 * */
public class WeakReferenceApp {
    public static void main(String[] args) throws Exception {
        /** 创建 `objA`强引用变量*/
        byte[] objA = new byte[1024 * 1024];
        /** 创建弱引用变量*/
        WeakReference<byte[]> reference = new WeakReference<>(objA);
        if (reference.get() == null) {
            System.out.println("reference is null");
        } else {
            System.out.println("reference is " + reference.get());
        }
        /** 将强引用删除(为了与`弱引用对象 reference`的联系给失效), 然后调用垃圾回收*/
        objA = null;
        System.gc();
        /** 由于 Finalizer线程优先级较低, 暂停2秒, 为了保证 GC的执行*/
        Thread.sleep(2000);
        /** 创建 `objB`强引用变量
         * - 创建以下数据的同时, 由于老年代空间不足, 会触发 Full GC, 但是容量依然不够, 所以会将弱引用删除, 以此维持此次的执行进程
         * */
        byte[] objB = new byte[1024 * 1024];

        System.out.println("reference is " + reference.get());
    }
}

输出:
[GC (Allocation Failure) [PSYoungGen: 512K->384K(1024K)] 512K->384K(3584K), 0.0013732 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 892K->464K(1024K)] 892K->464K(3584K), 0.0014486 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 974K->464K(1024K)] 974K->472K(3584K), 0.0011306 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
reference is [B@610455d6
[GC (System.gc()) [PSYoungGen: 584K->512K(1024K)] 1616K->1552K(3584K), 0.0010554 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 512K->0K(1024K)] [ParOldGen: 1040K->388K(2560K)] 1552K->388K(3584K), [Metaspace: 3242K->3242K(1056768K)], 0.0061729 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 504K->368K(1024K)] 892K->764K(3584K), 0.0110091 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
reference is null # 弱引用被释放因此输出 null

虚引用(Phantom Reference)

  • 一个对象是否有虚引用, 完全不会对其生命周期构成影响(最弱的引用), 也无法通过虚引用来获得一个对象的实例
  • 虚引用必须与引用队列一起使用. 如果垃圾收集器回收时, 发现有虚引用, 就会回收对象后, 将虚引用对象加入到引用队列中
  • 对象设置虚引用的唯一目的就是能在这个对象被回收时收到一个事件(就是对象回收跟踪的)
  • 实例:

public class PhantomReferenceApp {
    public static void main(String[] args) throws Exception {
        /** 创建引用队列, 为绑定虚引用*/
        ReferenceQueue referenceQueue = new ReferenceQueue();
        /** 创建 `app`强引用*/
        PhantomReferenceApp app = new PhantomReferenceApp();
        /** 创建虚引用*/
        PhantomReference data = new PhantomReference(app, referenceQueue);
        System.out.println("before gc: " + data.get() + ", " + referenceQueue.poll());
        /** 将强引用删除, 然后调用垃圾回收*/
        app = null;
        System.gc();
        /** 由于 Finalizer线程优先级较低, 暂停2秒, 为了保证 GC的执行*/
        Thread.sleep(2000);
        System.out.println("after gc: " + data.get() + ", " + referenceQueue.poll());
    }
}

输出:
> before gc: null, null # 获取不到任何东西 null
> after gc: null, java.lang.ref.PhantomReference@610455d6 # 但是引用队列收到了相关事件

终结器引用(Final Reference)

  • 终结器引用的实现就是 Jdk的 finalization机制, 将重写了 finalize()方法的对象加入到 F-Queue队列(引用队列)中, 并赋予3种状态: 可触及的, 可复活的和不可触及的. 使用详情参考: https://blog.csdn.net/qcl108/article/details/108835348

如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

猜你喜欢

转载自blog.csdn.net/qcl108/article/details/108856806