深入了解JAVA 软引用

这篇文章主要介绍了JAVA 软引用的相关资料,帮助大家更好的理解和学习,感兴趣的朋友可以了解下

定义

软引用是使用SoftReference创建的引用,强度弱于强引用,被其引用的对象在内存不足的时候会被回收,不会产生内存溢出。

说明

软引用,顾名思义就是比较“软”一点的引用。

一个对象与GC Roots之间存在强引用时,无论何时都不会被GC回收掉。如果一个对象与GC Roots之间没有强引用与其关联而存在软引用关联时,那么垃圾回收器对它的态度就取决于内存的紧张程度了。如果内存空间足够,垃圾回收器就不会回收这个对象,但如果内存空间不足了,它就难逃被回收的厄运。

如果一个对象与GC Roots之间不存在强引用,但是存在软引用,则称这个对象为软可达(soft reachable)对象。

垃圾回收器没有回收它的时候,软可达对象就像强可达对象一样,可以被程序正常访问和使用,但是需要通过软引用对象间接访问,需要的话也能重新使用强引用将其关联。所以软引用适合用来做内存敏感的高速缓存。

String s = new String("Frank"); // 创建强引用与String对象关联,现在该String对象为强可达状态 SoftReference softRef = new SoftReference(s); // 再创建一个软引用关联该对象 s = null; // 消除强引用,现在只剩下软引用与其关联,该String对象为软可达状态 s = softRef.get(); // 重新关联上强引用

这里变量s持有对字符串对象的强引用,而softRef持有对该对象的软引用,所以当执行s = null后,字符串对象就只剩下软引用了,这时如果因为内存不足发生Full GC,就会把这个字符串对象回收掉。

注意,在垃圾回收器回收一个对象前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要先判断返回是否为null,以免出现NullPointerException异常而导致应用崩溃。

下面的代码会让s再次持有对象的强引用:

s = softRef.get();

如果在softRef指向的对象被回收前,用强引用指向该对象,那这个对象又会变成强可达。

来看一个使用SoftReference的栗子:

public class TestA { static class OOMClass{ private int[] oom = new int[1024 * 100];// 100KB } public static void main(String[] args) throws InterruptedException { ReferenceQueue queue = new ReferenceQueue(); List list = new ArrayList(); while(true){ for (int i = 0; i (new OOMClass(), queue)); } Thread.sleep(500); } } }

注意,ReferenceQueue中声明的类型为OOMClass,即与SoftReference引用的类型一致。

设置一下虚拟机参数:

-verbose:gc -xms4m -Xmx4m -Xmn2m

运行结果:

[GC (Allocation Failure) 1017K->432K(3584K), 0.0017239 secs]

[GC (Allocation Failure) 1072K->472K(3584K), 0.0099237 secs]

[GC (Allocation Failure) 1323K->1296K(3584K), 0.0009528 secs]

[GC (Allocation Failure) 2114K->2136K(3584K), 0.0009951 secs]

[Full GC (Ergonomics) 2136K->1992K(3584K), 0.0040658 secs]

[Full GC (Ergonomics) 2807K->2791K(3584K), 0.0036280 secs]

[Full GC (Allocation Failure) 2791K->373K(3584K), 0.0032477 secs]

[Full GC (Ergonomics) 2786K->2773K(3584K), 0.0034554 secs]

[Full GC (Allocation Failure) 2773K->373K(3584K), 0.0032667 secs]

[Full GC (Ergonomics) 2798K->2775K(3584K), 0.0036231 secs]

[Full GC (Allocation Failure) 2775K->375K(3584K), 0.0055482 secs]

[Full GC (Ergonomics) 2799K->2776K(3584K), 0.0031358 secs]

...省略n次GC信息

在TestA中,我们使用死循环不断的往list中添加新对象,如果是强引用,会很快因为内存不足而抛出OOM,因为这里的堆内存大小设置为了4M,而一个对象就有100KB,一个循环添加100个对象,也就是差不多10M,显然一个循环都跑不完就会内存不足,而这里,因为使用的是软引用,所以JVM会在内存不足的时候将软引用回收掉。

[Full GC (Allocation Failure) 2791K->373K(3584K), 0.0032477 secs]

从这一条可以看出,在内存不足发生Full GC时,回收掉了大部分的软引用指向的对象,释放了大量的内存。

因为这里新生代只分配了2M,所以很快就会发生GC,如果你的程序运行没有看到这个结果,请先确认一下虚拟机参数是否设置正确,如果设置正确还是没有看到,那么将循环次数由1000改为10000或者100000在试试看。

应用场景

软引用关联的对象,只有在内存不足的时候JVM才会回收该对象。这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。

现在考虑这样一个场景 ,在很多应用中,都会出现大量的图片,比如说QQ的默认头像,应用内的认图标等等,这些图片很多地方会用到。

如果每次都去读取图片,由于读取文件速度较慢,大量重复的读取会导致性能下降。所以可以考虑将图片缓存起来,需要的时候直接从内存中读取。但是,由于图片占用内存空间比较大,缓存的图片过多会占用比较多的内存,就可能比较容易发生OOM。这时候,软引用就派得上用场了。

注意,SoftReference对象是用来保存软引用的,但它同时也是一个Java对象。所以,当软可及对象被回收之后,虽然这个SoftReference对象的get()方法返回null,但SoftReference对象本身并不是null,而此时这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。

ReferenceQueue就是用来保存这些需要被清理的引用对象的。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

下面用SoftReference来实现一个简单的缓存类:

public class SoftCache { // 引用队列 private ReferenceQueue referenceQueue = new ReferenceQueue(); // 保存软引用集合,在引用对象被回收后销毁 private List> list = new ArrayList(); // 添加缓存对象 public synchronized void add(T obj){ // 构建软引用 Reference reference = new SoftReference(obj, referenceQueue); // 加入列表中 list.add(reference); } // 获取缓存对象 public synchronized T get(int index){ // 先对无效引用进行清理 clear(); if (index reference = list.get(index); return reference == null ? null : reference.get(); } public int size(){ return list.size(); } @SuppressWarnings("unchecked") private void clear(){ Reference reference; while (null != (reference = (Reference) referenceQueue.poll())){ list.remove(reference); } } }

然后测试一下这个缓存类:

public class SoftCacheTest { private static int num = 0; public static void main(String[] args){ SoftCache softCache = new SoftCache(); for (int i = 0; i 上一篇:Java正则表达式匹配电话格式下一篇:Spring事物的传播特性详解 热门搜索

深入了解 

深入详解 

深入理解 

软引用 

深入了解网络蠕虫 

相关文章

深入了解JAVA 软引用

2021-09-10阅读(3425)评论(0)推荐()

这篇文章主要介绍了JAVA 软引用的相关资料,帮助大家更好的理解和学习,感兴趣的朋友可以了解下

深入理解Java中的弱引用

2021-10-12阅读(3374)评论(0)推荐()

这篇文章主要介绍了深入理解Java中的弱引用,本文讲解了强引用、弱引用、引用队列、四种引用、软引用、虚引用等内容,需要的朋友可以参考下

深入了解JAVA 虚引用

2021-10-19阅读(7645)评论(0)推荐()

这篇文章主要介绍了JAVA 虚引用的相关资料,帮助大家更好的理解和学习JAVA,感兴趣的朋友可以了解下

Java中强引用,软引用,弱引用概念解析

2021-10-12阅读(6464)评论(0)推荐()

这篇文章主要介绍了Java中强引用,软引用,弱引用概念解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

java软引用在浏览器使用实例讲解

2021-10-12阅读(5210)评论(0)推荐()

在本篇文章里小编给大家整理的是一篇关于java软引用在浏览器使用实例讲解内容,有兴趣的朋友们可以学习下。

Java多线程通信问题深入了解

2021-09-10阅读(9204)评论(0)推荐()

下面小编就为大家带来一篇深入理解JAVA多线程之线程间的通信方式。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

深入了解Java核心类库--Objects类

2021-09-10阅读(6744)评论(0)推荐()

这篇文章主要介绍了Java中的Object类详细介绍,本文讲解了Object类的作用、Object类的主要方法、Object类中不能被重写的方法、Object类...

取消

有人回复邮件通知

提交评论

© 2021 编程之家 

工信部备案号:琼ICP备2022000316号

相关文章

HashMap是Java中最常用的集合类框架,也是Java语言中非常典型...
在EffectiveJava中的第 36条中建议 用 EnumSet 替代位字段,...
介绍 注解是JDK1.5版本开始引入的一个特性,用于对代码进行说...
介绍 LinkedList同时实现了List接口和Deque接口,也就是说它...
介绍 TreeSet和TreeMap在Java里有着相同的实现,前者仅仅是对...
HashMap为什么线程不安全 put的不安全 由于多线程对HashMap进...