JVM中的五种引用

一 强引用

        我们平时使用的大多数对象引用,都是强引用,如下obj就是一个强引用,强引用的特点是:如果所有的GC ROOTS对象都没有与某个对象有强引用路径,那么这个对象就可以被垃圾回收了。

Object obj=new Object();

二 软引用

        通过SoftReference引用对象时,这个引用就是软引用。软引用的特点是:如果一个对象仅存在软引用,当进行GC后,如果内存依旧不够使用,则在下次进行GC时,回收软引用引用的对象。软引用自身也是一个对象(SoftReference类型的对象),也会占用内存空间,SoftReference对象的回收需要使用引用队列。

package com.tech.gc.reference;

import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

/**
 * 演示软引用
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 * @author lw
 * @since 2021/11/4
 */
public class Demo_1 {
    private static final int _4Mb=4*1024*1024;

    public static void main(String[] args) throws IOException {
//        List<byte[]> list=new ArrayList<>();
//        for (int i = 0; i < 5; i++) {
//            list.add(new byte[_4Mb]);
//        }
//        System.in.read();
        soft();
    }

    public static void soft(){
//        list-->SoftReference-->byte[]
        List<SoftReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            SoftReference<byte[]> softReference = new SoftReference<byte[]>(new byte[_4Mb]);
            System.out.println(softReference.get());
            list.add(softReference);
            System.out.println(list.size());
        }
        System.out.println("循环结束:"+list.size());
        for (int i = 0; i < list.size(); i++) {
            SoftReference<byte[]> softReference = list.get(i);
            System.out.println(softReference.get());
        }
    }
}

 分配了20M的堆内存空间,往list中添加4M的内存数组,添加5次,如果不使用软引用的方式,会出现内存溢出错误。

 使用软引用的方式,日志如下:

"C:\Program Files\Java\jdk1.8.0_251\bin\java.exe" -Xmx20m -XX:+PrintGCDetails -verbose:gc "-javaagent:D:\Tech\resource\IntelliJ IDEA 2018.3.1\lib\idea_rt.jar=57163:D:\Tech\resource\IntelliJ IDEA 2018.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_251\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_251\jre\lib\rt.jar;D:\Tech\code\tech-jvm\target\classes;C:\Users\lw\.m2\repository\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;C:\Users\lw\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.12.4\jackson-annotations-2.12.4.jar;C:\Users\lw\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.12.4\jackson-core-2.12.4.jar;C:\Users\lw\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.12.4\jackson-databind-2.12.4.jar" com.tech.gc.reference.Demo_1
[B@135fbaa4
1
[B@45ee12a7
2
[B@330bedb4
3
[GC (Allocation Failure) [PSYoungGen: 1995K->488K(6144K)] 14283K->12999K(19968K), 0.0009764 secs] [Times: user=0.05 sys=0.01, real=0.00 secs] 
[B@2503dbd3
4
[GC (Allocation Failure) --[PSYoungGen: 4696K->4696K(6144K)] 17208K->17224K(19968K), 0.0008141 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 4696K->4579K(6144K)] [ParOldGen: 12527K->12442K(13824K)] 17224K->17021K(19968K), [Metaspace: 3275K->3275K(1056768K)], 0.0046699 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) --[PSYoungGen: 4579K->4579K(6144K)] 17021K->17069K(19968K), 0.0007473 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 4579K->0K(6144K)] [ParOldGen: 12490K->619K(8704K)] 17069K->619K(14848K), [Metaspace: 3275K->3275K(1056768K)], 0.0061145 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[B@4b67cf4d
5
循环结束:5
null
null
null
null
[B@4b67cf4d
Heap
 PSYoungGen      total 6144K, used 4377K [0x00000000ff980000, 0x0000000100000000, 0x0000000100000000)
  eden space 5632K, 77% used [0x00000000ff980000,0x00000000ffdc66b8,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
 ParOldGen       total 8704K, used 619K [0x00000000fec00000, 0x00000000ff480000, 0x00000000ff980000)
  object space 8704K, 7% used [0x00000000fec00000,0x00000000fec9ae98,0x00000000ff480000)
 Metaspace       used 3283K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 357K, capacity 388K, committed 512K, reserved 1048576K

Process finished with exit code 0

在添加第4个元素时,内存分配失败,触发GC(minor gc),回收了一部分空间,第4个元素添加成功;

在添加第5个元素时,内存分配失败,触发GC(minor gc),没有回收到空间,又触发了一次Full GC,几乎没有回收到空间,又触发一次GC(minor gc),没有回收到空间,由触发了一次Full GC,回收了大部分空间,根据打印的日志可以知道应该是前4个软引用对象被回收了。第5个元素添加成功。

所以最后打印日志:前面4个元素为null,最后一个不是null。

我们在list中存入的是软引用对象(SoftReference对象),软引用对象通过软引用引用着byte[]数组,我们想让byte[]回收时SoftReference也被回收,可以通过如下方式实现:

package com.tech.gc.reference;

import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

/**
 * 演示软引用 引用对象(SoftReference对象)被清理
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 * @author lw
 * @since 2021/11/4
 */
public class Demo_2 {
    private static final int _4Mb=4*1024*1024;

    public static void main(String[] args) throws IOException {
//        List<byte[]> list=new ArrayList<>();
//        for (int i = 0; i < 5; i++) {
//            list.add(new byte[_4Mb]);
//            System.out.println(list.size());
//        }
//        System.in.read();
        soft();
    }

    public static void soft(){
//        list-->SoftReference-->byte[]

        ReferenceQueue<byte[]> referenceQueue=new ReferenceQueue<>();

        List<SoftReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            //当软引用引用的对象,这里是byte[]被回收时,软引用自身(SoftReference对象)会被添加到引用队列ReferenceQueue中
            SoftReference<byte[]> softReference = new SoftReference<byte[]>(new byte[_4Mb],referenceQueue);
            System.out.println(softReference.get());
            list.add(softReference);
            System.out.println(list.size());
        }
        System.out.println("循环结束:"+list.size());

        //只要出现在引用队列中,说明这个软引用引用的对象正在被垃圾回收
        //清楚list中已经被回收的软引用(SoftReference对象)
        Reference<? extends byte[]> reference = referenceQueue.poll();
        while (reference!=null){
            list.remove(reference);
            reference=referenceQueue.poll();
        }

        for (int i = 0; i < list.size(); i++) {
            SoftReference<byte[]> softReference = list.get(i);
            System.out.println(softReference.get());
        }
    }
}

通过运行结果可以知道,list只是强引用了1个SoftReference,其他的SoftReference由于没有强引用也会被回收。

三 弱引用

        通过WeakReference引用对象,这种引用方式为弱引用。它的特点是:在进行GC时,无论内存是否充足,都会回收弱引用对象。弱引用本身(WeakReference对象)也会占用内存,它的回收需要使用引用队列。

弱引用的使用如下:

package com.tech.gc.reference;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

/**
 * 演示弱引用
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 * @author lw
 * @since 2021/11/4
 */
public class Demo_3 {
    private static final int _4MB=4*1024*1024;

    public static void main(String[] args) {
        // list-->WeakReference-->byte[]
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[_4MB]);
            list.add(weakReference);
            for(WeakReference<byte[]> wrf:list){
                System.out.print(wrf.get()+" ");
            }
            System.out.println();
        }
        System.out.println("循环结束:"+list.size());
    }
}

四 虚引用

        使用PlantomReference引用对象时,这种引用方式就是虚引用,虚引用需要结合引用队列ReferenceQueue使用,比如NIO中的ByteBuffer就使用到了虚引用来实现在ByteBuffer对象GC时,释放它申请的直接内存。它使用到了Cleaner对象,它继承于PlantomReference也是一个虚引用,在ByteBuffer通过Unsafe申请直接内存后,会创建Container对象,并把内存地址传递给Container对象,Contanner对象通过虚引用引用了ByteBuffer对象,在ByteBuffer对象被GC时虚引用Container对象会进入引用队列,在引用队列中有一个ReferenceHandler线程会定时扫描引用队列,发现Container对象就会调用它的相关方法,通过Unsafe释放直接内存。

五 终结器引用

        使用FinalReference引用对象,这种引用方式为终结器引用。需要结合引用队列来使用。finalize方法的调用就使用到了终结器引用。当一个对象正在被回收时(还没有回收),虚拟机会创建该对象的终结器引用,并且进入引用队列,在引用队列上有一个优先级很低的线程,扫描到终结器引用,会根据引用找到对象,调用对象的finalize方法,实现资源清理,在下次GC时该对象会被回收。由于使用finalize的对象在被回收时,需要两次,且调用finalize的线程优先级特别低,finalize方法可能迟迟不能调用,所以不推荐使用finalize方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值