在过去一年中,我在我的应用程序的Java堆使用方面取得了巨大的进展 - 实现了坚实的66%的减少。为此,我一直在通过SNMP监视各种指标,例如Java堆大小、CPU、Java非堆等等。
最近,我一直在监视JVM消耗的实际内存(RSS,常驻集)并感到有些惊讶。JVM消耗的实际内存似乎完全独立于我的应用程序堆大小、非堆、伊甸园空间、线程数等等。
Java SNMP测量的堆大小 Java堆使用图表 http://lanai.dietpizza.ch/images/jvm-heap-used.png
占用的实际内存(以KB为单位,例如:1 MB的KB = 1 GB) Java RSS图表 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 Wed Sep 24 ... 2008 x86_64 ... GNU / Linux)
- Java 6(构建1.6.0_07-b06)
- Tomcat 6
- 应用程序(按需HTTP视频流)
- 通过java.nio FileChannels进行高I/O
- 几百到几千个线程
- 低数据库使用率
- Spring、Hibernate
相关的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通过使用pmap
指导我正确的诊断路径。 (请为他们的答案投票!)以下是发生的情况:
我确定的事情:
- 我的应用程序使用JRobin 1.4记录和显示数据,这是我三年前编码到我的应用程序中的。
- 当前应用程序最繁忙的实例会在启动后的一小时内创建超过1000个新的JRobin数据库文件(每个文件大小约为1.3MB),每天会创建大约100个以上。
- 如果有数据需要写入,则应用程序每15秒更新这些JRobin数据库对象一次。
- 在默认配置中,JRobin:
- 使用基于
java.nio
的文件访问后端。 此后端将MappedByteBuffers
映射到文件本身。 - 每5分钟,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
我可能没有时间弄清楚的问题:
MappedByteBuffer.force()
在JVM内部发生了什么? 如果没有任何更改,它是否仍然写入整个文件? 部分文件? 它先加载吗?- MBB的一定数量始终保持在RSS中吗? (RSS大约是总分配MBB大小的一半。 这是巧合吗? 我怀疑不是。)
- 如果将
MappedByteBuffer.force()
移到数据库更新事件中而不是周期性计时器中,这个问题是否会神奇地消失? - RSS