闲来无事,用Java的软引用写了一个山寨的缓存
今天杭州的天气是异常的炎热,35度,这是要我卿命呢,加之最近心情极其差劲,scala的东西也一段时间没看没写了。今天公司又是有犬吠、又是有人结婚、又是有小孩的哭啼、又是有杭州限电公司空调也关了、又是由于XXX原因登境外网站完全崩溃,因此深刻觉得没有Google的程序员不如去卖红薯。
好了闲话少说进入正题,众所周知java中的引用分为StrongReference、SoftReference、WeakReference、PhantomReference。这几种引用有不同那个的使用场景,平时我们用的最频繁的也就是StrongReference也就是说形如之这样的引用:
?Object obj = new Object();
?这种引用就是所谓的强引用,如果此对象没有引用指向它,并且活着的线程无法访问到它(针对垃圾孤岛而言),那么他才会被回收,如果该对象被强引用指向,并且内存被耗尽,抛出OOM垃圾收集器也不会回收该对象。
而对于SoftReference而言它被GC回收的条件就没那么严格了,如果一个对象当前最强的引用是软引用,并且JVM的内存充足,垃圾回收器是不会回收的该对象的。只有在内存比较吃紧的情况下GC才会回收被软引用指向的对象,从这个特点我们就看出了软引用可以用来做一些高速缓存,配合LRU策略更好。今天我就自己写一个玩玩。
T obj = null;//如果当前Key对应的软引用的对象被回收则移除该KeyReference<? extends T> reference = null;while((reference = this.referenceQueue.poll()) != null){obj = reference.get();if(obj == null){this.cachedReference.remove(((InnerReference<K, T>)reference).getKey());}}}public void clearALLObject(){this.cachedReference.clear();System.gc();}}??在整个实现中通过将对象的引用放入我定义的一个key->软引用map中,然后每次从cache中获取对象时,首先通过key去查询map获得对象的软引用,若存在则通过软引用去尝试获取对象,若不存在,软引用指向的对象被回收,那么我们就回去调用内置的handler,重新生成一个对象,并cache该对象的软引用。
在我的实现中我为用户提供了一个当对象被回收时的处理handler,企图来指导用户通过这个handler来重新构造对象,缓存对象,灵活性还是挺大的。
不足之处就是,如果软引用的缓存能用LRU策略更完美了,再为LRU提供一个Processor,用于用户自定义LRU策略。其实很简单只要将HashMap换成LinkedHashMap去实现removeEldest方法,并在方法中调用自定义的LRU处理器就OK了。
? ? ? ? 为了减少开销,我在每次cache的时候才去清理已经失效的软引用。也许有人会问为啥有个ReferenceQueue呢?其实是这样的,在软引用所引用的对象被回收以后,试想想对象软引用的对象是被回收了,但是你又引入了另一个对象SoftReference,带走一个难道还要再留下一个,所以不会的,软引用对象被回收后,这个软引用本身被添加到了这个queue,等待回收。通过便利这个queue获取软引用来一出map中过期的软引用。
至此,该说的也都说了,不该说的也说了,结尾很突兀,敬请见谅!
?
?
4 楼 shansun123 2011-05-11 hxrs 写道阿里集体结婚?~应该是 5 楼 blackbeans 2011-05-11 shansun123 写道hxrs 写道阿里集体结婚?~
应该是
还是你厉害
6 楼 olylakers 2011-05-11 blackbeans 写道shansun123 写道hxrs 写道阿里集体结婚?~
应该是
还是你厉害
看到好几个阿里的朋友抱怨了。。。。 7 楼 kakaluyi 2011-05-11 假如key不存在,lz你用poll就强制把queue清空最老的数据,这个是你要的结果吗?
private void cleanReference(K key) {
Reference<? extends T> reference = null;
while((reference = this.referenceQueue.poll()) != null)
{
T obj = reference.get();
if(obj == null)
{
this.cachedReference.remove(key);
}
}
}
8 楼 yeswoyaofei 2011-05-11 private void cleanReference(K key) 这个函数没看懂。能解释一下吗 9 楼 beneo 2011-05-11 this.referenceQueue.poll() 出来的 reference 与你的 key 应该不是对应的,这么写是为什么?
referenceQueue只是一个node-list,而且enqueue顺序与gc有关,LZ如何拿key与reference关联? 10 楼 Rooock 2011-05-11 想法是挺好的. 不过貌似大伙都不喜欢画图说话.
11 楼 blackbeans 2011-05-11 kakaluyi 写道假如key不存在,lz你用poll就强制把queue清空最老的数据,这个是你要的结果吗?
private void cleanReference(K key) {
Reference<? extends T> reference = null;
while((reference = this.referenceQueue.poll()) != null)
{
T obj = reference.get();
if(obj == null)
{
this.cachedReference.remove(key);
}
}
}
恩这里考虑的不周到,意图是利用ReferenceQueue来断定软引用引用的对象是否被回收来决定删除key到Softreference的映射,看来写的有问题呵呵,感谢提出建议 12 楼 blackbeans 2011-05-11 yeswoyaofei 写道private void cleanReference(K key) 这个函数没看懂。能解释一下吗
代码已经修改了,之前考虑的不周到,尽请见谅
13 楼 blackbeans 2011-05-11 beneo 写道this.referenceQueue.poll() 出来的 reference 与你的 key 应该不是对应的,这么写是为什么?
referenceQueue只是一个node-list,而且enqueue顺序与gc有关,LZ如何拿key与reference关联?
代码已经修改了,我将创建软引用的时候就去记录这个Key,遍历整个queue的时候就可以去删除对应的key-SoftReference的映射了
14 楼 beneo 2011-05-11 其实你不知道object reference 何时 enqueue,所以你的代码还是有问题 15 楼 blackbeans 2011-05-11 beneo 写道其实你不知道object reference 何时 enqueue,所以你的代码还是有问题
为什么要关注 合适进去queue里呢?我只关心,弱引用被放入queue,检查object有没有被回收,被回收就删除映射,就好啦
16 楼 何处烤地瓜 2011-05-11 你在那里的上班啊? 华星路? 还是滨江 17 楼 blackbeans 2011-05-11 何处烤地瓜 写道你在那里的上班啊? 华星路? 还是滨江
不告诉你哈哈 18 楼 beneo 2011-05-11 blackbeans 写道beneo 写道其实你不知道object reference 何时 enqueue,所以你的代码还是有问题
为什么要关注 合适进去queue里呢?
1. 创建一个 object 并且放入 reference
2. put key, reference, referenceQueue in your cache
3. the object = null, and if the softreference do not enqueue
问题 reference,object = null,此时并没有enqueu, 调用cachedReference,结果本来就出问题了
19 楼 sarstime 2011-05-12 闲来无事 是亮点。 20 楼 fmjsjx 2011-05-12 为什么要用HashMap呢?按照楼主用softReference的思路,用WeakHashMap不更合适么。
而且楼主好像也没处理并发问题,至少要加个读写锁才能用吧…… 21 楼 blackbeans 2011-05-12 fmjsjx 写道为什么要用HashMap呢?按照楼主用softReference的思路,用WeakHashMap不更合适么。
而且楼主好像也没处理并发问题,至少要加个读写锁才能用吧……
写来玩玩而已,并发的方面我没有考虑,只是个想法,提供基本的cache功能,然后提供一个Handler来处理对象被回收的情况。 22 楼 snwz 2011-05-12 您好 我想请教下
在第七行代码
# if(obj == null)
# {
# this.cachedReference.remove(((InnerReference<K, T>)reference).getKey());
# }
为什么要在obj为空时候才remove?
还有就是while 循环里ref = this.queue.poll() 怎么就能确定获取的就是当前要删除的 ref呢? 23 楼 blackbeans 2011-05-12 snwz 写道您好 我想请教下
在第七行代码
# if(obj == null)
# {
# this.cachedReference.remove(((InnerReference<K, T>)reference).getKey());
# }
为什么要在obj为空时候才remove?
还有就是while 循环里ref = this.queue.poll() 怎么就能确定获取的就是当前要删除的 ref呢?
这个跟gc有关,对于软引用当gc去回收软引用所引用的对象同时,也会去将软引用本身放到所谓的referceQueue中,来回收,不然虽然回收了软引用所引用的对象,但是产生了额外的SoftReference的垃圾