Java的五大引用

五大引用概述

基于可达性算法的垃圾回收

image-20221113125807100

  • 实线是强引用,虚线是其他引用

强引用

image-20221113121216580

  • 只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
  • 我们平时用new出来的对象,用一个引用来指向这个对象,这个引用是强引用

软引用

image-20221113123644044

  • 通过GC Root对象强引用了我们的软引用对象,然后用软引用对象指向一个对象,这个对象就是被软引用指向的对象
  • 软引用自身也是会占内存的
  • 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次触发垃圾回收,回收软引用对象,可以配合引用队列来释放软引用自身

演示

/**
 * 演示软引用
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class Demo2_3 {

    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();


    }
	//这是我们的软引用
    public static void soft() {
        // list --> SoftReference --> byte[]

        List<SoftReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());

        }
        System.out.println("循环结束:" + list.size());
        for (SoftReference<byte[]> ref : list) {
            System.out.println(ref.get());
        }
    }
}

强引用执行的结果

image-20221113153150968

  • 会发生推内存溢出的现象,因为都是强引用,所以垃圾回收无法回收

软引用执行的结果

image-20221113153338607

  • 在第五次进行分配空间的时候,内存不够了,第一次进程垃圾回收发现内存还是不够,所以触发第二次的垃圾回收对软引用对象进行回收,所以发现前四个byte数组的内存地址为null

演示配合引用队列对软引用本身进行回收

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

/**
 * 演示软引用, 配合引用队列
 */
public class Demo2_4 {
    private static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) {
        List<SoftReference<byte[]>> list = new ArrayList<>();

        // 引用队列
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

        for (int i = 0; i < 5; i++) {
            // 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }

        // 从队列中获取无用的 软引用对象,并移除
        Reference<? extends byte[]> poll = queue.poll();
        while( poll != null) {
            list.remove(poll);
            poll = queue.poll();
        }

        System.out.println("===========================");
        for (SoftReference<byte[]> reference : list) {
            System.out.println(reference.get());
        }

    }
}

  • queue队列就是我们的引用队列,当我软引用所关联的byte数组对象被回收,软引用就会加到queue中

弱引用

image-20221113124758706

  • 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象,可以配合引用队列来释放弱引用自身

演示

public class Code_09_WeakReferenceTest {

    public static void main(String[] args) {
//        method1();
        method2();
    }

    public static int _4MB = 4 * 1024 *1024;

    // 演示 弱引用
    public static void method1() {
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for(int i = 0; i < 10; i++) {
            WeakReference<byte[]> weakReference = new WeakReference<>(new byte[_4MB]);
            list.add(weakReference);

            for(WeakReference<byte[]> wake : list) {
                System.out.print(wake.get() + ",");
            }
            System.out.println();
        }
    }

    // 演示 弱引用搭配 引用队列
    public static void method2() {
        List<WeakReference<byte[]>> list = new ArrayList<>();
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

        for(int i = 0; i < 9; i++) {
            WeakReference<byte[]> weakReference = new WeakReference<>(new byte[_4MB], queue);
            list.add(weakReference);
            for(WeakReference<byte[]> wake : list) {
                System.out.print(wake.get() + ",");
            }
            System.out.println();
        }
        System.out.println("===========================================");
        Reference<? extends byte[]> poll = queue.poll();
        while (poll != null) {
            list.remove(poll);
            poll = queue.poll();
        }
        for(WeakReference<byte[]> wake : list) {
            System.out.print(wake.get() + ",");
        }
    }

}


弱引用执行的结果

image-20221113160053296

虚引用

虚引用有个最重要的应用就是我们的直接内存分配的ByteBuffer

image-20221113143059909

  • 必须配合引用队列使用,在上的ByteBuffer对象中的cleaner对象就是我们的虚引用,存储的是我们的直接内存的地址,然后我们的ReferenceHandler线程调用cleaner对象的clean方法,然后调用了unsafe.freeMemory来释放我们的直接内存

  • 因为直接内存不受JVM的内存自动管理的控制,所以通过这些方法来间接的实现对直接内存的回收

终结器引用

image-20221113145110661

  • finallize()方法是我们的Object中的方法,所以所有对象都有这个方法
  • 重写了了finalize方法,A4对象第一次垃圾回收的时候并不是立马被回收,因为重写了finallize方法,所以虚拟机会自动创建一个终结器引用,执行A4对象的第一次回收的时候,会将终结器引用放入引用队列(不会回收A4对象)
  • 由我们的Finalizer线程通过终结器引用找到被引用的对象,并调用他的finallize方法,第二次GC时才会回收这个A4对象
  • 不推荐使用
    • 因为处理引用队列的线程优先级很低,被处理的机会很少,可能造成这个对象的finallize()迟迟不被调用,导致这块内存迟迟不被回收
posted @ 2022-11-13 21:23  刘颂成  阅读(58)  评论(0编辑  收藏  举报