Java reference详解

1 篇文章 0 订阅
1 篇文章 0 订阅

最近在做一个服务编排执行引擎的东西,其中业务逻辑执行的参数上下文存储在了ThreadLocal里面,为了保证运行参数不丢失,对ThreadLocal进入了一些源码研究,发现实际的存储是在ThreadLocalMap里面,而map的中key居然是weakreference类型,这能保证GC的时候,业务运行参数不丢失吗?由此,对不同的引用reference做了一下深入了解,机制是什么?应用的场景是什么?

概述:

  • reference的目的是为了让程序能够对JVM的垃圾回收施加一定影响,或者说一种交互机制,且这种影响是有限的。
  • 每种reference对象都继承自Reference类,其有一个referent属性,用于存储被引用对象。GC在做垃圾回收的时候,回收的就是这个referent对象。
  • 通过 reference的get方法获取真正的被引用对象。
  •  一个对象只有失去所有强引用以后,才有可能根据不同引用类型执行不同的回收策略。

引用类型主要分为四种:

  • Strong reference:

String strRefer = new String("referece");

GC在标记阶段,从root set出发,被遍历到的强引用对象不会被垃圾回收。

  • soft reference:

String strRefert = new String("referece");

SoftReference<String> softRef = new SoftReference<String>(strRefert);

strRefert = null;

System.gc();

System.out.printlin("is garbage collect ========" + softRef.get());

对于软引用,GC有一套自己的策略,只要没有发生out of space,GC就有可能不回收软引用对象。

  • weak reference:

String strRefert = new String("referece");

WeakReference<String> weakRef = new WeakReference<String>(strRefert);

strRefert = null;

System.gc();

System.out.printlin("is garbage collect ========" + weakRef.get());

对于弱引用,只要发生GC,引用对象就会被垃圾回收

  • phantom reference:

行为与weakreference一致,区别在于生成phantom时,必须传入referenceQuene对象。GC回收对象时,如果引用类型是weakreference类型,会在执行被引用对象前的finalize前,将weakreference对象推入referenceQuene;而如果是phantomreference类型,则是先执行被引用对象的finalize,然后再将phantomreference对象推入referenceQuene。phantomreference的应用场景很少。

那么referenceQuene又是什么鬼?

以上面的代码为例,GC做了垃圾回收动作,那么程序怎么知道referent到底有没有被回收呢?一个一个对象的去遍历,是不是low了点儿?referenceQuene帮我们做了统一的收口,如果我们在创建弱引用对象的时候,传入了referenceQuene参数:

String strRefert = new String("referece");

ReferenceQuene<String> refQuene = new ReferenceQuene<String>();

WeakReference<String> weakRef = new WeakReference<String>(strRefer, refQuene);

strRefert = null;

System.gc();

System.out.printlin("is garbage collect ========" + weakRef.get());

System.out.printlin("is enquened ========" + weakRef.isEnqueued());//是否是已经被入列

System.out.printlin("weakRef ========" + refQuene.pull());

System.out.printlin("is same instance ========" + (weakRef == refQuene.pull()));

System.out.printlin("weakRef ========" + refQuene.remove());

那么GC做完垃圾回收以后,会将reference对象push到ReferenceQuene里面,这样我们知道reference引用的referent被垃圾回收了,也表明此时的reference对象也没有任何意义了,需要我们对其进行清理,或者做一些其他的资源清理工作。

ReferenceQuene主要有两个方法:

poll():如果存在reference对象,则拉取并从队列中删除,如果没有则返回null,不阻塞

remove():阻塞方法,直到有reference对象为止。

说了半天,应用场景跟目的到底是啥:

看了很多篇文章,大部分都说用作缓存,Map的方式,用weakReference将key进行一次包裹,作为map的key,那么当key失去强引用以后,GC会对weakReference包裹的key进行回收清除,这样value也就可以被清除了;很多人对此提出了质疑,key都没有了,还怎么进行下次查询,缓存的意义何在?我也深以为然!所以,个人认为,还是为了内存不会出现out of space。oracle的官网举了个列子,就是我们浏览大量的图片,还翻页,还不知道会不会往回翻页,图片很大,加载很慢,这时我们可以考虑用弱引用,往回翻页,存在正好,不存在了则再重新加载一遍。问题又来了,图片的缓存肯定会以图片名称作为key,图片的实际内容作为value,这会是什么样的一种结构?

weakhashmap描述:

如上所述的map,Java给我们提供了WeakHashMap类,其key值就是被weakReference所包裹的可以被GC回收的,问题又来了,图片的名称是key,但我们实际上要回收的是value,真正的图片内容,且WeakHashMap宣称是自动回收的,这个是怎么实现的呢?通过研究源码发现原来在对WeakHashMap做任何操作的时候,put、get、size、toString等等,都会首先调用一个expungeStaleEntries方法,仔细分析其源码,原来这个方法会pull对应的referenceQuenue,把已经清除的key找出来,然后再删除对应的value值,其所宣贯的自动清除,实际上是在调用它的方法时触发的。那么ThreadLocalMap又怎么保证虽然weakReference作为key,但执行过程中不被清除的呢?

ThreadLocalMap实现:

看注解:

 /**

 ThreadLocalMap is a customized hash map suitable only for maintaining thread local values. No operations are exported outside of the ThreadLocal class. The class is package private to allow declaration of fields in class Thread.  To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys. However, since reference queues are not used, stale entries are guaranteed to be removed only when the table starts running out of space.

*/

根本就没有清除value!

 

参考文献:

http://www.fis.unipr.it/lca/tutorial/java/refobjs/index.html

http://learningviacode.blogspot.com/2014/02/reference-queues.html

https://community.oracle.com/blogs/enicholas/2006/05/04/understanding-weak-references

https://stackoverflow.com/questions/7136620/weak-references-how-useful-are-they

https://stackoverflow.com/questions/5511279/what-is-a-weakhashmap-and-when-to-use-it

https://docs.oracle.com/javase/6/docs/api/java/lang/ref/package-summary.html#reachability

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值