您的位置:首页 > Web前端

实战SoftReference被回收的时机

2016-08-10 00:18 323 查看
这是我最近在开发的一个基于客户端发现模式(因为基于服务端发现的都比较多了,consul还做得很好)的服务注册发现框架:

  https://github.com/leoChaoGlut/ServiceDIscoveryAndRegistry

然后在过程中遇到这样一个问题:

在做client-service-proxy的时候,要实现一个负载均衡算法.

我选了比较常用的轮询分发算法.

在实现过程中,需要将发送过的相同的host和port缓存下来,以便不要再次分发到相同的节点.如果一个服务的所有节点都发送过了,则会清理缓存,重新轮询.

问题来了,实现缓存,如果直接用new HashSet()这样的方式存储,如果内部的元素一直被引用,和GC Root一直有通路,则该对象不会被GC.

但有时候该对象其实又不会经常被用到,存着还是浪费内存.

所以一般会用SoftReference<Set<T>>软引用方式来做缓存容器.

SoftReference的源码介绍里也有提到,它可以被用作memory-sensitive caches.

问题又来了,SoftReference被GC的情况和想象中不一样!!!!!!!!!!!!!

我在测试的时候,用VisualVM来监控测试程序,当我利用VisualVM手动执行Full GC时,SoftReference竟然没有被GC!!!!!!!!

于是乎我又去看了源码的解释,如下:

All soft references to softly-reachable objects are guaranteed to have been cleared before the virtual machine throws an <code>OutOfMemoryError</code>.

意思是在OOM发生之前,它才会被GC掉,而没有发生OOM之前的GC,是不会将SoftReference的对象GC掉的!

会导致的问题:

SoftReference<Set<String>> recordSetRef = new SoftReference<>(new HashSet<>());//1
Set<String> recordSet = recordSetRef.get();//2
if (recordSet.isEmpty()) {//3. 如果运气非常不好...这里可能会报空指针.因为执行到上面那一句的时候,该线程正好失去CPU时间片,且正好即将发生OOM(正常的GC不会清理SoftReference),则此时recordSet会是null
}


根据Java 内存模型:一个代码块内,如果前后的代码不存在关联关系,则可能会发生指令重排序.(当然,上例中没有出现指令重排序).

当然,一个线程对应一个方法栈,如果两个线程之间的数据没有依赖关系,也是不会发生并发线程安全问题的.

如果要避免这样的情况,可以将该代码块用synchronized包裹起来,如果频繁被调用的话,不建议加锁,应该另辟出路.

因为加了锁,就算当前线程失去时间片,在该线程没有执行完同步块之前,其它线程是无法访问其中的数据的.等到线程重新获得时间片,可以继续执行未执行完的代码.

问题在三到来:也许有人想到了,如果加了同步块,其它线程不能访问其中的内容,那要是GC线程呢?好吧,这我也不知道,等我知道了我再告诉你........

有知道的朋友希望可以解答~谢谢~

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息