【发布时间】:2011-04-26 22:12:10
【问题描述】:
这是一个内存堆栈(用作缓存),仅由静态 ConcurrentHashMap (CHM) 组成。
所有传入的 HTTP 请求数据都存储在这个 ConcurrentHashMap 中。并且有一个异步调度进程,它从同一个 ConcurrentHashMap 中获取数据,并在将它们存储到数据库后删除 key.value。
该系统运行良好且流畅,但仅在以下条件下发现,内存已充分利用(2.5GB)并且所有 CPU 时间都用于执行 GC:
-1000/s 的并发 http 命中率
-在 15 分钟内保持相同的并发命中
每次写入数据库时,异步进程都会记录 CHM 的剩余大小。 CHM.size() 维持在 Min:300 到 Max:3500 左右
我认为此应用程序存在内存泄漏。所以我使用 Eclipse MAT 来查看堆转储。运行嫌疑人报告后,我从 MAT 获得了这些 cmets:
“org.apache.catalina.loader.StandardClassLoader @ 0x853f0280”加载的“org.apache.catalina.session.StandardManager”的一个实例占用了 2,135,429,456 (94.76%) 字节。内存在“”加载的“java.util.concurrent.ConcurrentHashMap$Segment[]”的一个实例中累积。
3,646,166 instances of java.util.concurrent.ConcurrentHashMap$Segment retain >= 2,135,429,456 bytes.
和
Length # Objects Shallow Heap Retained Heap
0 3,646,166 482,015,968 >= 2,135,429,456
上面的长度 0 我将其转换为 CHM 内的空长度记录(每次我调用 CHM.remove() 方法时)。与数据库内的记录数一致,创建此转储时数据库内有3,646,166条记录
奇怪的场景是:如果我暂停压力测试,堆内存中的利用率会逐渐释放到 25MB。这大约需要 30-45 分钟。我重新模拟了这个应用程序,曲线看起来类似于下面的 VisualVM 图表:
问题如下:
1) 这看起来像内存泄漏吗?
2) 每次删除调用 remove(Object key, Object value) 以从 CHM 中删除 <key:value>,该删除的对象是否会获得 GC?
3) 这与 GC 设置有关吗?我添加了以下 GC 参数但没有帮助:
-XX:+UseParallelGC
-XX:+UseParallelOldGC
-XX:GCTimeRatio=19
-XX:+PrintGCTimeStamps
-XX:ParallelGCThreads=6
-verbose:gc
4) 非常感谢任何解决此问题的想法! :)
新 5)有可能因为我所有的参考都是硬参考吗?我的理解是,只要 HTTP 会话结束,所有那些不是静态的变量现在都可用于 GC。
新 注意我尝试用 ehcache 2.2.0 替换 CHM,但我遇到了同样的 OutOfMemoryException 问题。我想ehcache 也在使用ConcurrentHashMap。
服务器规格:
-Xeon 四核,8 线程。
-4GB 内存
-Windows 2008 R2
-Tomcat 6.0.29
【问题讨论】:
-
用 EhCache 的实例替换哈希映射有多难?这些库针对此类任务进行了优化。
-
目前我们尽量不对现有代码进行太多更改,因为我们还没有分析影响。 EhCache 最初是考虑的一部分,但不知何故没有被选为实现选择。
标签: java