这篇文章我们来聊聊软引用和弱引用对内存性能的帮助,大家在平时的开发过程中,对于内存性能做过哪些调优工作,其中的一个方法就是我们可以使用软引用和弱引用。
软引用和弱引用的定义
- 软引用(SoftReference):
如果一个对象只具有软引用,而当前虚拟机堆内存空间足够时,那么垃圾回收器就不会回收它,反之就会回收当前软引用指向的对象。
- 弱引用(WeakReference):
垃圾回收器发现一个对象上只有弱引用,此时不管内存够不够,这个对象都会被回收。
下面通过一段简单的代码来看一下软引用和弱引用的具体用法;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
public class ReferenceDemo {
public static void main(String[] args) {
//强引用
String str = new String("abc");
//弱引用
SoftReference<String> softRef = new SoftReference<>(str);
str = null; //去掉强引用
System.gc(); //垃圾回收器进行回收
System.out.println(softRef.get());
//强引用
String abc = new String("123");
//弱引用
WeakReference<String> weakRef = new WeakReference<>(abc);
abc = null; //去掉强引用
System.gc(); //垃圾回收器进行垃圾回收
System.out.println(weakRef.get());
}
}
通过上面这个例子,我们来分析一下软引用和弱引用的差别,首先看弱引用的例子,我们先定义了一个强引用对象,然后我们给这个强引用对象加了一个软引用,这里要注意软引用的写法是SoftReference
,然后我们通过str = null来去掉str对象的强引用,此时str这个对象只有一个软引用,通过System.gc()
;我们进行了一次垃圾回收,因为str这个对象只有一个弱引用,没有强引用,而这是内存足够,所以说这个对象是不会被垃圾回收器回收的,它的输出是abc。
然后我们看第二个例子,首先定义了一个强引用对象,然后给这个对象加了一个弱引用,这里给对象加弱引用的方法是WeakReference
,我们再通过abc = null,去掉这个对象的强引用,此时abc这个对象就只有一个弱引用,通过System.gc()
进行了一次垃圾回收,这个适合abc这个对象没有强引用,只有一个弱引用,根据弱引用的定义,在垃圾回收之后,这个对象会被回收的,所以说弱引用的输出结果是null。
那么最终的结果是不少我们分析的这样,我们run一下看看,如下图所示,只有弱引用的对象在内存空间足够的时候没有被回收,对象在只有弱引用的时候被垃圾回收器回收。
软引用的使用场景:
比如说在一个博客管理系统里,为了提升访问性能,用户在点击博文时,如果这篇博文有缓存,这样其他用户在点击这篇博文时,就直接从内存中加载,而不走数据库,而这样可以降低响应时间,首先,我们定义一个Content类,来封装博文的内容,
然后我们定义一个类型为HashMap<String,SoftReerence<Content>>
对象来保存缓存内容,其中键是String类型表示文章id,值是一个指向Content的软连接,随后当用户点击某个id时,用id去HashMap中找,如果找到,并且Content的内容不为空,那么从中拿数据并直接做展示动作,这样的话,不要查询数据库,可以提高性能。在其中用id找不到,或者虽然找到,但是其中内容为空,那么我们就从数据库里面去找,找到文章后同时把它插入到HashMap这个缓存中,这个地方要注意,插入缓存后要删除Content上的强引用,从而保证只有一个软引用。
这样用软引用的好处,假设我们用1GB的内存,缓存了10000篇博文,那么这10000篇博文,在内存空间上只有软引用,没有强引用,如果内存空间足够时,那么我们可以通过缓存来提升性能,但是万一内存不够,我们可以依次释放这10000篇博文占用的内存,而释放时不会影响业务流程,最多就是稍微影响性能。
对比一下,如果这里我们用强引用来做缓存,会有什么后果,由于我们不知道什么时候该撤销在Content上的强引用,所以说找不到一个合适的机会来释放缓存。如果我们再引入一套缓存机制,这就属于额外工作了,就没有像软引用这样直接了。
弱引用的使用场景:
在某个电商网站中,我们会用Coupon类来保存优惠券信息,比如我们其中可以定义优惠券打折程度,有效期和作用范围等等,当我们从数据库中,得到所有的优惠券信息之后,会用一个List<Coupon>类型来保存优惠券,这个时候,如果我们想保存优惠券和它关联的用户时,我们可以用WeakHashMap<Coupon,List<WeakReference<USer>>>
这样类型的weakCouponHM对
象,这样的对象,它主体是WeakHashMap
,也就是基于弱引用的HashMap,其中键是Coupon
类型,值是指向List<WeakReference<User>>
的弱引用,想象一下,如果有一百个优惠券,那么它会存储在List<Coupon>
类型中的couponList
中,同时在WeakHashMap
这个类型对象里,也会用键的形式存储这100个优惠券。加入有10000个用户,我们可以用List<User>
类型的userList
对象来保存它们。
我们假设coupon1这个优惠券对应100个用户,那么我们可以通过weakCouponHM.put()
方法来添加它们的对于关系,其中是以弱引用的方法, 保存了coupon1所对应的100个用户,同理,当假设某个优惠券3,用弱引用的方式指向这100个用户,当某个用户注销账户时,它会从List<User>
这个对象中删除,换句话说,这个对象就只有在weakCouponHM
里面的值,也就是只有一个弱引用,在下次垃圾回收的时候回被清除,这样coupon3上关联的用户它会被自动更新成99个。
相比之下,我们不用弱引用而选择用强引用,那么我们就在用户被删除之后,需要手动删除用户和优惠券之间的对应关系,如果忘了删除就会出现代码问题,相比之下,当我们引用弱引用时,就会给我们带来“自动更新”这样的好处。
更多Java相关文章、资料,可以关注公众号故里学Java,回复资源包获取