为什么即使堆等大小稳定,Sun JVM 也会继续消耗更多的 RSS 内存?问题和解决方案:
在过去的一年里,我对应用程序的Java堆使用率有了很大的改进- 减少了66%。为了做到这一点,我一直在通过SNMP监控各种指标,例如Java堆大小,cpu,Java非堆等。
最近,我一直在监视JVM有多少真实内存(RSS,驻留集),并且有些惊讶。JVM 消耗的实际内存似乎完全独立于我的应用程序堆大小、非堆、eden 空间、线程数等。
由 Java SNMP Java Heap Used Graph http://lanai.dietpizza.ch/images/jvm-heap-used.png 测量的堆大小
真实内存(以 KB 为单位)。(例如:1 MB 的 KB = 1 GB)Java 堆使用的图形 http://lanai.dietpizza.ch/images/jvm-rss.png
(堆图中的三个斜率对应于应用程序更新/重新启动。
这对我来说是一个问题,因为JVM消耗的所有额外内存都是“窃取”操作系统可用于文件缓存的内存。事实上,一旦RSS值达到~2.5-3GB,我开始看到我的应用程序响应时间变慢,CPU利用率更高,主要是IO等待。因为某些点对交换分区的分页开始生效。这都是非常不可取的。
所以,我的问题:
- 为什么会发生这种情况?“引擎盖下”发生了什么?
- 我能做些什么来控制JVM的实际内存消耗?
血腥的细节:
- RHEL4 64 位 (Linux - 2.6.9-78.0.5.ELsmp #1 SMP 星期三 九月 24 ...2008 x86_64...GNU/Linux)
- Java 6 (build 1.6.0_07-b06)
- 雄猫 6
- 应用程序(点播 HTTP 视频流)
- 通过 java.nio FileChannels 实现高 I/O
- 数百到低数千个线程
- 数据库使用率低
- 春天,冬眠
相关 JVM 参数:
-Xms128m
-Xmx640m
-XX:+UseConcMarkSweepGC
-XX:+AlwaysActAsServerClassMachine
-XX:+CMSIncrementalMode
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime
-XX:+CMSLoopWarn
-XX:+HeapDumpOnOutOfMemoryError
我如何测量 RSS:
ps x -o command,rss | grep java | grep latest | cut -b 17-
这进入一个文本文件,并定期读入监控系统的RRD数据库。请注意,ps 输出千字节。
问题和解决方案:
虽然最终是ATorras的答案被证明是最终正确的,但kdgregory通过使用.kdgregory引导我走上正确的诊断路径。(去投票给他们的答案!以下是正在发生的事情:pmap
我确切知道的事情:
- 我的应用程序使用JRobin 1.4记录和显示数据,这是我三年前在我的应用程序中编码的东西。
- 当前创建的应用程序最忙的实例
- 在启动后的一小时内,超过1000个新的JRobin数据库文件(每个约1.3MB)
- 启动后每天约 100+
- 该应用程序每15秒更新一次这些JRobin数据库对象,如果有东西要写的话。
- 在默认配置JRobin中:
- 使用基于文件访问的后端。此后端映射到文件本身。
java.nio
MappedByteBuffers
- 每五分钟一次,一个JRobin守护进程线程调用每个JRobin底层数据库MBB
MappedByteBuffer.force()
- 使用基于文件访问的后端。此后端映射到文件本身。
-
pmap
上市:- 6500 映射
- 其中5500个是1.3MB JRobin数据库文件,最高可达约7.1GB
最后一点是我的“尤里卡!”时刻。
我的纠正措施:
- 考虑更新到最新的JRobinLite 1.5.2,这显然更好
- 在 JRobin 数据库上实现正确的资源处理。目前,一旦我的应用程序创建了一个数据库,然后在数据库不再被主动使用后永远不会转储它。
- 尝试将 移动到数据库更新事件,而不是定期计时器。问题会神奇地消失吗?
MappedByteBuffer.force()
- 立即将JRobin后端更改为 java.io 实现 - 行更改。这会慢一些,但可能不是问题。下图显示了此更改的直接影响。
Java RSS 内存使用的图形 http://lanai.dietpizza.ch/images/stackoverflow-rss-problem-fixed.png
我可能有时间或可能没有时间弄清楚的问题:
- JVM 内部发生了什么?如果没有任何更改,它是否仍会写入整个文件?文件的一部分?它会先加载它吗?
MappedByteBuffer.force()
- RSS 中是否始终存在一定数量的 MBB?(RSS 大约是分配的 MBB 总大小的一半。巧合?我怀疑不是。
- 如果我移动到数据库更新事件,而不是定期计时器,问题会神奇地消失吗?
MappedByteBuffer.force()
- 为什么 RSS 斜率如此规律?它与任何应用程序负载指标都不相关。