• M.java(一个普通的类,被回收时调用重写的finalize()方法
public class M {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize");
    }
}

一、强引用

正常new的引用就是强引用,若有一个强引用指向一个对象,则该对象不会被回收

public class T01_NormalReference {
    public static void main(String[] args) throws IOException {
        M m = new M();
        m = null;
        System.gc(); //DisableExplicitGC

        System.in.read();//阻塞,防止main方法终止
    }
}
  • 结果
    • 若m不设为null,则没有被回收
    • 若m设为null,m被回收

二、软引用

  • 软引用是用来描述一些还有用但并非必须的对象。
  • 对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行第二次回收。
  • 若内存充足则不会回收
  • 如果这次回收之后仍没有足够的内存,才会抛出内存溢出异常。
  • 软引用非常适合缓存使用
//设置参数为-Xms20M -Xmx20M
public class T02_SoftReference {
    public static void main(String[] args) {
        SoftReference<byte[]> m = new SoftReference<>(new byte[1024 * 1024 * 10]);
        System.out.println(m.get());//未被回收
        System.gc();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(m.get());//未被回收

        //再分配一个数组,heap将装不下,这时候系统会垃圾回收,先回收一次,如果不够,会把软引用干掉
        byte[] b = new byte[1024 * 1024 * 15];
        System.out.println(m.get());//被回收
    }
}
  • 打印结果
[B@19dfb72a
[B@19dfb72a
null

三、弱引用

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

应用实例为TreadLocal

public class T03_WeakReference {
    public static void main(String[] args) {
        WeakReference<M> m = new WeakReference<>(new M());

        System.out.println(m.get());
        System.gc();
        System.out.println(m.get());

        ThreadLocal<M> tl = new ThreadLocal<>();
        tl.set(new M());
        tl.remove();//必须remove,防止map里内存泄漏
    }
}

  • 打印结果
com.mashibing.juc.c_022_RefTypeAndThreadLocal.M@19dfb72a
null
finalize

四、虚引用

  • 一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象的实例。
  • 为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。
  • 虚引用和弱引用对关联对象的回收都不会产生影响,如果只有虚引用关联着对象,那么这个对象就会被回收。它们的不同之处在于弱引用的get方法,虚引用的get方法始终返回null,
  • 弱引用可以使用ReferenceQueue,虚引用必须配合ReferenceQueue使用。

  • jdk中直接内存的回收就用到虚引用,由于jvm自动内存管理的范围是堆内存,而直接内存是在堆内存之外(其实是内存映射文件,自行去理解虚拟内存空间的相关概念),所以直接内存的分配和回收都是有Unsafe类去操作,java在申请一块直接内存之后,会在堆内存分配一个对象保存这个堆外内存的引用,这个对象被垃圾收集器管理,一旦这个对象被回收,相应的用户线程会收到通知并对直接内存进行清理工作。

  • 事实上,虚引用有一个很重要的用途就是用来做堆外内存的释放,
  • DirectByteBuffer就是通过虚引用来实现堆外内存的释放的。
    批注 20200605 005522.jpg
public class T04_PhantomReference {
    private static final List<Object> LIST = new LinkedList<>();
    private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();

    public static void main(String[] args) {
        PhantomReference<M> phantomReference = new PhantomReference<>(new M(), QUEUE);

        new Thread(() -> {
            while (true) {
                LIST.add(new byte[1024 * 1024]);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
                System.out.println(phantomReference.get());
            }
        }).start();

        new Thread(() -> {
		//监控QUEUE中是否有回收的对象的通知
            while (true) {
                Reference<? extends M> poll = QUEUE.poll();
                if (poll != null) {
                    System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);
                }
            }
        }).start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

hhhhh