【发布时间】:2014-11-22 14:21:56
【问题描述】:
我有一个系统,其中许多线程生成要插入到 NoSql 后端的日志。为了减少网络流量,我在服务器和后端之间引入了一个缓冲区。
环境是:
Java、JSP、Spring MVC、JDK 1.7 Apache-tomcat-6
使用的缓冲区是java中的ConcurrentLinkedQueue。还实现了一个 DBPushThread 以每 5 秒从队列中获取日志并将它们插入到后台。我们使用 offer() 进行插入,使用 poll() 进行弹出。根据 poll() - https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html#poll%28%29 的 javadoc,它将检索元素并更新队列的头部。所以这个节点永远不会被引用并最终被垃圾回收。
我运行了服务器 1 天,发现服务器随着时间的推移变得过于缓慢。使用 JVisualVM 对服务器进行堆转储(hprof),并在分析时观察到有超过 15,000,00 个 ConcurrentLinkedQueue$Node 对象实例。在检查实例视图时,我可以看到大多数对象的 LinkedList 节点值(属性“item”)及其对下一个节点(属性“next”)的引用设置为 null。意味着这些 Node 对象是垃圾收集的候选对象,但它没有发生并且取消引用的 Node 对象堆积在内存中。
加码sn-p
public void add(Log log) {
buffer.offer(log);
}
从队列中检索内容(这里总是将最大索引指定为队列大小)
public List<Log> getContents(int maxIndex) {
List<Log> logs = new LinkedList<Log>();
for (int i = 0; i < maxIndex; i++) {
Log log = buffer.poll();
logs.add(Log);
}
return logs;
}
我只将缓冲区(这是单例队列)作为实例变量。所有其他都是函数的本地范围。
这是 JDK 1.7 的一个错误,即废弃的节点永远不会被垃圾收集吗?
或
我需要在 ConcurrentLinkedQueue 中实现对象池吗?如果是这样,我该如何实现?
或
这是我的代码的错误吗?
请指导。
【问题讨论】:
-
您应该准确说明“十万”的含义,或者对泄露的实例使用实际数字。我不得不用谷歌搜索this definition 并理解它的意思是 100,000(十万)。请允许 SO 读者之间存在文化差异。
-
好的。我已经更正了。
标签: java memory-leaks jvm java.util.concurrent