在过去的一年里,我在应用程序的 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 堆使用图 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)
- 雄猫 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引导我使用pmap
. (去投票给他们的两个答案!)这是发生了什么:
我肯定知道的事情:
- 我的应用程序使用JRobin 1.4记录和显示数据,这是我三年多前在我的应用程序中编写的代码。
- 当前创建的应用程序最繁忙的实例
- 启动后一小时内有超过 1000 个新的 JRobin 数据库文件(每个大约 1.3MB)
- 开机后每天~100+
- 如果有要写的东西,应用程序每 15 秒更新一次这些 JRobin 数据库对象。
- 在默认配置 JRobin 中:
- 使用
java.nio
基于 - 的文件访问后端。此后端映射MappedByteBuffers
到文件本身。 - 每五分钟一次,JRobin 守护线程调用
MappedByteBuffer.force()
每个 JRobin 底层数据库 MBB
- 使用
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 斜率如此规则?它与任何应用程序负载指标无关。