Java中的引用类型

强引用

强引用是Java中最常见的引用方式。如果对象是强引用的,内存溢出也不会回收。

Object o = new Object();
结束对象的生命周期,这个对象才会被垃圾回收,或者显式的设置

o = null;

软引用

内存足够时,软引用对象不会被回收,内存不够时才会被回收。如果回收了软引用的对象之后仍然没有足够的内存,才会抛出内存溢出异常。

//设置最大堆内存为:-Xmx15M
//直接声明的对象是强引用,软引用必须通过SoftReference对象。SoftReference对象在内存中时软引用
SoftReference<byte[]> m = new SoftReference<>(new byte[1024 * 1024 * 10]);
//获取软引用的对象
System.out.println(m.get());
//执行垃圾回收
System.gc();
//暂停500毫秒,留给垃圾回收一点时间
Thread.sleep(500);
//再次输出一下数据,看是否被回收
System.out.println(m.get());
//再使用6m的堆内存,此时堆空间不足,会触发GC
byte[] temp = new byte[1024 * 1024 * 6];
//获取不到数据:null
System.out.println(m.get());
软引用可以用来实现缓存

弱引用

无论内存是否足够,只要JVM开始垃圾回收,弱引用关联的对象都会被回收。画外音:不能存在强引用可达


//定义的buff是强引用
byte[] buff = new byte[1024 * 1024 * 10];
//定义弱引用
WeakReference<byte[]> weakReference = new WeakReference<>(buff);
//触发一次GC
System.gc();
Thread.sleep(500);
//此时还可以获取到弱引用的对象
System.out.println(weakReference.get());
//准备回收str对象
buff = null;
byte[] buff2 = new byte[1024 * 1024 * 10];
System.out.println(weakReference.get());

输出:

[B@1b6d3586
null
上面的例子中,buff是强引用,而定义的弱引用weakReference在触发一次GC之后也可以获取到数据。因为此时数据可以由buff(强引用)和weakReference(弱引用)同时可达。

此时设置buff为null,等待垃圾回收。此时我们设置的最大堆空间还是:-Xmx15M,再创建一个buff2数组,大小为10M,这时堆空间不足会引发GC。垃圾回收会回收强引用buff。

内存中仅弱引用weakReference可达,因此会被垃圾回收,再次输出时变为null。

弱引用用于ThreadLocal中,ThreadLocalMap的key存放弱引用对象,当存放的对象失效时(线程没了),key也会变为null。ThreadLocalMap的get()和set()方法也会删除key为null的数据。

虚引用

一般用于管理直接内存(堆外内存)

这里以以DirectByteBuffer为例,介绍虚引用。DirectByteBuffer主要用于创建堆外内存,和另一种创建堆外内存的方式Unsafe#allocateMemory不同,DirectByteBuffer可以使用JVM自动管理内存(画外音:JVM也可以管理堆外内存),DirectByteBuffer管理堆外内存的方式就是通过虚引用。

DirectByteBuffer初始化时会创建一个Cleaner对象,用于堆外内存回收。Cleaner对象继承了PhantomReference。PhantomReference需要配合引用队列ReferenceQueue使用

如下图所示,Cleaner对象在初始化时会使用头插法加入Cleaner链表中。first是Cleaner类中的静态变量,用指向头结点。DirectByteBuffer对象包含对外内存地址、大小以及Cleaner对象的引用。ReferenceQueue队列用于保存要回收的Cleanner对象。 
Java中的引用类型

当发生GC时,DirectByteBuffer对象回收,Cleaner对象不再有引用关系,在下一次GC时u,Cleanner对象会被加入到ReferenceQueue队列中,并执行clean()方法。 

Java中的引用类型
clean()方法主要做两件事:

1、将Cleaner对象在Cleaner链表中删除

2、调用thunk()的run方法,该线程的run方法内部会调用unsafe的freeMemory方法,用于释放堆外内存。

原文始发于微信公众号(Java不惑):Java中的引用类型

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/24520.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!