引言:
java中、软引用和弱引用的概念以及作用非常晦涩难懂。因为不经常使用,就算是经验丰富的程序员也搞不清楚这两个概念。本文将用一个场景通过白话方式阐述这两个概念的含义以及作用。
正文:
以前有个村子,村子里面有个地主,地主和他老婆经常会雇佣短工给自己打工。刚好村子外面有座山,山上住着一些淳朴的山民。地主就经常去山上找一些山民给自己做短工。这些被雇佣的山民就住在地主家干活。农忙的时候,地主就多雇了几个,可过了农忙季节,也不需要这么多短工了,地主他老婆就时不时把这些多余的短工赶回山上去,只留一部分干活的。等过段时间,再去山上叫人。这些山民也老实,地主每次来叫都会跟着地主来打会工,地主老婆说让他们回去,他们也老老实实回去。就这样,虽然来来回回有些麻烦,大家都觉得还挺好。
后来有个算命先生来了,见了此番场景,盘了盘手里的核桃,对地主说:“你每次去山里叫这些人下山给你打工,路上可要不少盘缠,一路跟着你回家也要花不少时间。要是这些人没活干,你叫你老婆别急着赶他们走,在村口搭个棚子,稍微给他们些吃穿用度,让他们先在村口候着。要是突然来事情了,你直接在外面叫他们来干活。这不就省下了去山上叫他们的来回盘缠和时间了吗?”。算命先生看见地主听得出神,微微一笑,得意地45°角看天空,小毡帽掉下来,露出来秃顶的脑袋。
地主回了神,眼里冒着精光,赶紧捡起地上的小毡帽给算命先生递过去。
“先生妙计,这个法可真绝!可...”。
“可什么?”。
“搭个棚子是没什么难的,可天天供着他们,这也是不小的开销。”。
“那还不简单,不需要天天养着,你要是农忙活多,让你老婆先别急着赶人,叫他们多住几日。就这几天的吃食,可比来回上山的开销省多了。再者,你让你老婆赶人的时候,看着点,要是哪些老弱病残在大棚住着,就尽快赶他们走,要是些精壮的汉子,经常用到他们,那就再缓缓。”
最后地主就采取了算命先生的方法,发现真的又省时间又省事。农忙的时候部分短工会闲下来住到村口大棚,又有活干了,就又叫回去。遇到地主家也没什么余粮的时候,再把大棚里的人统统赶走。
故事说完了,其实地主就是jvm,地主老婆就是gc线程,短工就是对象实例,去山上叫人就是jvm创建对象。至于大棚区的住户,就是被软引用(精壮男人)和弱引用(老弱病残)的工人。
若对象(短工)没有软引用和弱引用,就直接就被gc了(没资格住大棚区),若是弱引用(老弱病残),其实也会马上被gc的,优势就是在被gc前,可以重新回地主家打工成为强引用。若是软引用(精壮男人),不太会被gc掉(会比较长的待在大棚区),除非jvm内存gc多次发现内存还是不够用,那么也是会释放软引用的对象的(地主家实在也没余粮了)。
实际代码
自己写了两个内部类,重写toString,用来输出;对象简单点直接用字符串对象。
这里引入一个类:ReferenceQueue,它是会在对象被gc时,把对象的弱引用、软引用添加到这个队列中,就好像从大棚离开的工人排队迈上了返乡之路,。
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
class Scratch {
public static void main(String[] args) {
// soft();
weak();
}
/**
* “精壮”软引用
*/
public static void soft(){
ReferenceQueue queue = new ReferenceQueue();//“返乡队列”,被gc的对象会在这个队列中插入对象的软引用"SoftReference"
String work = new String("worker");//强引用:work
MySoftReference softReference = new MySoftReference(work,queue);
System.out.println(softReference.get());//根据软引用找到对象
work = null;//工人离开地主家工位,到地主家附近休息,即对象失去强引用,由于指定了软引用,因此会去大棚休息
System.out.println(softReference.get());//根据软引用找到对象,可在此处赋值给变量转化为强引用
System.gc();// 地主老婆来赶人,因为是“精壮的”软引用,【所以不会被gc,继续留在大棚区,这里和弱引用不同】
System.out.println(queue.poll());//对象【未】被gc,则poll结果为null
System.out.println(softReference.get());//由于是精壮的软引用,所以一次gc是不会被gc掉的,只有gc成功才会进返乡队列,所以这里poll得到的结果为null,若多次gc后,jvm认定当前空间紧缺,把软引用也gc了,此时poll会得到该对象的软引用
}
/**
* “老弱病残”弱引用
*/
public static void weak(){
ReferenceQueue queue = new ReferenceQueue();//“返乡队列”,被gc的对象会在这个队列中插入对象的弱引用"WeakReference"
String work = new String("worker");//强引用:work
MyWeakReference weakReference = new MyWeakReference(work,queue);
System.out.println(weakReference.get());//根据弱引用找到对象
work = null;//工人离开地主家工位,到地主家附近休息,即对象失去强引用,由于指定了弱引用,因此会去大棚休息
System.out.println(weakReference.get());//根据弱引用找到对象,可在此处赋值给变量转化为强引用
System.gc();// 地主老婆来赶人,因为是老弱病残的弱引用,【直接赶人】
System.out.println(queue.poll());//对象被gc,会往queue中放引用对象,则poll结果为引用对象
System.out.println(weakReference.get());//再根据弱引用去大棚找人肯定找不到了
}
public static class MyWeakReference extends WeakReference{
public MyWeakReference(Object referent) {
super(referent);
}
public MyWeakReference(Object referent, ReferenceQueue q) {
super(referent, q);
}
@Override
public String toString() {
return "MyWeakReference{}";
}
}
public static class MySoftReference extends SoftReference {
public MySoftReference(Object referent) {
super(referent);
}
public MySoftReference(Object referent, ReferenceQueue q) {
super(referent, q);
}
@Override
public String toString() {
return "MySoftReference{}";
}
}
}
可以用软引用和弱引用的特性做缓存,使用弱引用、软引用做缓存可重新利用即将被gc的对象,省去了创建对象的开销。软引用相比弱引用留存的时间更长。