强、软、弱、虚引用与垃圾回收

目录

什么是引用

引用的结构图

强引用

软引用

弱引用

虚引用

引用队列 ReferenceQueue


 

什么是引用

 

我们在java中经常会对类进行实例化

Person p = new Person

等号左边是对象的“引用”,存储在栈中。等号右边是实例化的对象,存储在堆中。

这种引用也被称为强引用。

 

 

引用的结构图

 

引用类分强软弱虚以及引用队列,它们都继承于Object类,结构图如下

强、软、弱、虚引用与垃圾回收_第1张图片

 

 

强引用

 

在Java中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。无论JVM的内存是否充足,进行垃圾回收时永远都不会回收强引用对象。正因为它永远都不会被GC回收,所以它会造成Java内存泄漏问题

当强引用被删除或被赋值为null时,就会被GC回收掉。

public class StrongReferenceDemo {

    public static void main(String[] args) {
        // 这样定义的默认就是强应用
        Object obj1 = new Object();

        // 使用第二个引用,指向刚刚创建的Object对象
        Object obj2 = obj1;

        // 置空
        obj1 = null;

        // 垃圾回收
        System.gc();

        System.out.println(obj1);

        System.out.println(obj2);
    }
}

输出结果可以看到,即使obj1被设置成null,调用gc回收时, obj2也还是没有被回收掉

null
java.lang.Object@14ae5a5

 

 

软引用

 

软引用需要用Java.lang.ref.SoftReference类来实现,可以让对象豁免一些垃圾收集,对于软引用的对象来讲:

  • 当系统内存充足时,它不会被回收
  • 当系统内存不足时,它会被回收

软引用通常在对内存敏感的程序中,比如高速缓存就用到了软引用,内存够用的时候就保留,不够用就回收。

软引用的应用场景:

如果有一个应用需要读取大量本地图片,存硬盘中读取太慢,存内存中可能会造成内存溢出。这时我们可以把对象放在“软引用的HashMap”中,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,有效避免了OOM问题

Map> imageCache = new HashMap>();

弱引用使用举例 

public class SoftReferenceDemo {

    /**
     * 内存够用的时候
     */
    public static void softRefMemoryEnough() {
        // 创建一个强应用
        Object o1 = new Object();
        // 创建一个软引用
        SoftReference softReference = new SoftReference<>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());

        o1 = null;
        // 手动GC
        System.gc();

        System.out.println(o1);
        System.out.println(softReference.get());
    }

    /**
     * JVM配置,故意产生大对象并配置小的内存,让它的内存不够用了导致OOM,看软引用的回收情况
     * -Xms5m -Xmx5m -XX:+PrintGCDetails
     */
    public static void softRefMemoryNoEnough() {

        System.out.println("========================");
        // 创建一个强应用
        Object o1 = new Object();
        // 创建一个软引用
        SoftReference softReference = new SoftReference<>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());

        o1 = null;

        // 模拟OOM自动GC
        try {
            // 创建30M的大对象
            byte[] bytes = new byte[30 * 1024 * 1024];
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(o1);
            System.out.println(softReference.get());
        }

    }

    public static void main(String[] args) {

        softRefMemoryEnough();

        softRefMemoryNoEnough();
    }
} 
  

内存够用时强软引用都存在,内存不够用,当我们手动给强引用置为null,强引用才会被回收。而软引用当内存不够时,会自动被回收。 

java.lang.Object@7f31245a
java.lang.Object@7f31245a

[GC (Allocation Failure) [PSYoungGen: 31K->160K(1536K)] 682K->811K(5632K), 0.0003603 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 160K->96K(1536K)] 811K->747K(5632K), 0.0006385 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 96K->0K(1536K)] [ParOldGen: 651K->646K(4096K)] 747K->646K(5632K), [Metaspace: 3488K->3488K(1056768K)], 0.0067976 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 646K->646K(5632K), 0.0004024 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 646K->627K(4096K)] 646K->627K(5632K), [Metaspace: 3488K->3488K(1056768K)], 0.0065506 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 

null
null

 

 

弱引用

 

不管内存是否够,只要有GC操作就会进行回收

弱引用需要用 java.lang.ref.WeakReference 类来实现,它比软引用生存期更短。对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管JVM的内存空间是否足够,都会回收该对象占用的空间。弱引用的底层是WeakHashMap。WeakHashMap和HashMap类似,只不过它的Key是使用了弱引用的,也就是说,当执行GC的时候,WeakHashMap中的key会进行回收。

 

public class WeakReferenceDemo {
    public static void main(String[] args) {
        Object o1 = new Object();
        WeakReference weakReference = new WeakReference<>(o1);
        System.out.println(o1);
        System.out.println(weakReference.get());
        o1 = null;
        System.gc();
        System.out.println(o1);
        System.out.println(weakReference.get());
    }
} 
  

 我们可以看到,当内存足够时,手动调用了一下GC后,弱引用就被回收了

java.lang.Object@14ae5a5
java.lang.Object@14ae5a5

[GC (System.gc()) [PSYoungGen: 5246K->808K(76288K)] 5246K->816K(251392K), 0.0008236 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 808K->0K(76288K)] [ParOldGen: 8K->675K(175104K)] 816K->675K(251392K), [Metaspace: 3494K->3494K(1056768K)], 0.0035953 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

null
null

 

 

虚引用

 

虚引用又称为幽灵引用,需要java.lang.ref.PhantomReference 类来实现。如果一个对象持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访问对象,虚引用必须和引用队列ReferenceQueue联合使用。虚引用在被垃圾回收前会在ReferenceQueue中保存一下,用来跟踪对象被垃圾回收的状态,应用会收到一个系统通知或后续添加进一步的处理。类似于Spring AOP里面的后置通知

PhantomReference的get方法总是返回null,因此无法访问对象的引用对象。

 

 

引用队列 ReferenceQueue

 

引用队列就相当于一个副本,你可以把强软弱虚引用手动放到引用队列中(虚引用使用时必须放到引用队列中)。 放到引用队列中的引用对象即使被垃圾回收后,也可以在引用队列中打印该对象的信息。

你可能感兴趣的:([JVM])