【小家java】java5新特性(简述十大新特性) 重要一跃
【小家java】java6新特性(简述十大新特性) 鸡肋升级
【小家java】java7新特性(简述八大新特性) 不温不火
【小家java】java8新特性(简述十大新特性) 饱受赞誉
【小家java】java9新特性(简述十大新特性) 褒贬不一
【小家java】java10新特性(简述十大新特性) 小步迭代
【小家java】java11新特性(简述八大新特性) 首个重磅LTS版本
本文不论述java中值传递和引用传递之间的问题(有需求的可移步理解java中值传递和引用传递),而重点讨论Java中提供了4个级别的引用:强应用、软引用、弱引用和虚引用。这四个引用定义在java.lang.ref的包下。讲述这个话题的原因,也是我第一次在集合框架里看到WeakHashMap而被带进来,闲话不多说,直接进入主题~
强引用( Final Reference):只要强引用还存在,垃圾收集器永远不会回收(JVM宁愿抛出OOM异常也不回收强引用所指向的对)被引用的对象。但是,强引用可能会造成可能导致内存泄露哦,这个在后续文章中会有说明
贴出JDK对强引用的源码:
class FinalReference<T> extends Reference<T> {
public FinalReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
从类定义中可以看出,只有一个构造函数。重点是:它是非public的类哦,因此我们并不能在外部调用此构造函数来create一个强引用呢。这得益于JVM的设计,各位看官可以想想为什么呢?
软引用(Soft Reference):是用来描述一些还有用但并非必须的对象。对于软引用对象,如果内存充足gc不会管它,如果内存不够了,它就不能幸免了。在 JDK 1.2 之后,提供了 SoftReference 类可以让调用者创建一个软引用。软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用
先看一个最简单的使用
public static void main(String[] args) {
Obj obj = new Obj(); //创建一个强引用
SoftReference<Obj> softRef = new SoftReference<>(obj);
softRef.get(); //此方法需要注意:若softRef没被回收,返回对obj的强引用,若被回收了,则返回null,所以不可靠,适合做缓存
}
此时,对于这个Obj对象,有两个引用路径,一个是来自SoftReference对象的软引用,一个来自变量softRef 的强引用,所以这个Obj对象是强可达的,所以softRef.get()永远是有返回值的。此加如下代码:
...省略上面代码...
obj = null; //删除掉强引用
这时,Obj对象就只剩下软引用了,是软可达的。这个时候如果用get(),返回值就有可能是null了,因此使用的时候要十分注意。但是Obj对象被回收后又出现问题:SoftReference成垃圾了,完全没用了,这个时候建议处理掉,否则可能造成内存泄漏现象。所以ReferenceQueue队列上场啦。
对象的内存回收的时候会经历一个过程,从Active->Pending->Enqueued->Inactive。pending状态就是等待着进入ReferenceQueue队列的这样一个状态,说白了它目前还没被回收,只是对象的引用(用户代码中的引用)被移除了,pending保存了这个引用,并且放进ReferenceQueue里(更详细的可咨询JVM的对象回收流程),so
public static void main(String[] args) {
//定义一个引用队列
ReferenceQueue<Obj> queue = new ReferenceQueue<>();
//创建一个强引用
Obj obj = new Obj();
//创建一个软引用,并且关联上引用队列
SoftReference<Obj> softRef = new SoftReference<>(obj, queue);
if ((softRef = (SoftReference<Obj>) queue.poll()) != null) {
//队列里存在 说明对象马上就要被回收了 所以顺势也把软引用对象干掉
softRef = null; //可参考expungeStaleEntries方法
}
}
从上可以看出,咱们就可以监听回收,然后doSomething了
弱引用(WeakReference):弱引用和软引用很像,当gc时,无论内存是否充足,都会回收被弱引用关联的对象。它也可以和ReferenceQueue配合使用。如果弱引用所引用的对象被JVM回收,这个弱引用就会被加入到与之关联的引用队列中
虚引用(关注使用场景)
虚引用(PhantomReference):虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期(java对象的生命周期)。一个对象与虚引用关联,则跟没有引用与之关联一样,所以get()方法永远返回null,在任何时候都可能被垃圾回收器回收。因此它必须和ReferenceQueue一起使用,否则没有任何意义
如果使用全局Map,必然会造成很大程度上的内存泄漏。鉴于软引用(弱引用)的特点,可以结合ReferenceQueue来实现高速缓存了,这样对内存也特别友好。下面介绍一个实例演示,让同学们有个感官上的认识
public static void main(String[] args) {
Map<Object, Object> map = new HashMap<>();
//放置1000个1M大小的对象
for (int i = 0; i < 1000; i++) {
byte[] bytes = new byte[1024 * 1024 * 8];
map.put(i, bytes);
}
//报错:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
System.out.println("map.size->" + map.size());
}
上面使用了强引用类型,就直接报错了,这是必然的OOM错误
Map<Object, Object> map = new HashMap<>();
for(int i = 0;i < 10000;i++) {
byte[] bytes = new byte[_1M];
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(bytes, referenceQueue);
map.put(weakReference, i);
}
System.out.println("map.size->" + map.size());
这里使用了weakReference对象,即当值不再被引用时,相应的数据被回收。另外使用一个守护线程不断地从队列中获取被gc的数据
Thread thread = new Thread(() -> {
try {
int cnt = 0;
WeakReference<byte[]> k;
while((k = (WeakReference) referenceQueue.remove()) != null) {
System.out.println((cnt++) + "回收了:" + k);
}
} catch(InterruptedException e) {
//结束循环
}
});
thread.setDaemon(true);
thread.start();
结果如下:
9992回收了:java.lang.ref.WeakReference@1d13cd4
9993回收了:java.lang.ref.WeakReference@118b73a
9994回收了:java.lang.ref.WeakReference@1865933
9995回收了:java.lang.ref.WeakReference@ad82c
map.size->10000
在这次处理中,map并没有因为不断加入的1M对象而产生OOM异常,并且最终size=10000。不过其中的key(即weakReference)对象中的byte[]对象却被回收了。即不断new出来的1M数组被gc掉了。
从打印的结果中,我们看到有9995个对象被gc回收了,意味着在map的key中,除了weakReference之外,没有我们想要的业务对象(只存在引用对象,不存在真实对象了)。所以这个时候为了节约内存,其实是可以把entry一起移除掉的,这里不做演示了,同学们可以自行试验
咱们最常用的肯定是强引用,但是java提供的另外几种引用类型也是很有必要了解的,在特殊的场合也非常好用,特别是对于内存敏感的一些业务常用,可以极大的提高内存使用率提升效率,也可以提升jvm的能力
##—-题后语—-
我的微信公众号也会持续推送技术干货,欢迎下方二维码扫码关注获取。
更多内容持续更新中,欢迎关注我的博客!
有任何问题,可以跟我留言讨论,欢迎指正,不胜感激。
有任何疑问,亦可扫码向我提我哟~