Tomcat进程因为交换空间用尽被Linux内核杀掉;不会出现任何JVM OutOfMemory错误。
Tomcat进程因为交换空间用尽被Linux内核杀掉;不会出现任何JVM OutOfMemory错误。
我正在对一个Tomcat服务器执行负载测试。该服务器具有10G物理内存和2G交换空间。在此之前,堆大小(xms和xmx)设置为3G,服务器运行良好。由于我仍然看到了很多剩余内存并且性能不好,我将堆大小增加到7G并再次运行了负载测试。这次我观察到物理内存很快被消耗完,系统开始使用交换空间。后来,Tomcat在用尽交换空间后崩溃了。我在启动Tomcat时包含了-XX:+HeapDumpOnOutOfMemoryError
,但我没有得到任何堆转储。当我检查/var/log/messages
时,我看到kernel: Out of memory: Kill process 2259 (java) score 634 or sacrifice child
。
为了提供更多信息,以下是当堆大小设置为3G和7G时,我从Linuxtop
命令看到的情况。
xms和xmx = 3G(运行良好):
- 在启动Tomcat之前:
Mem: 10129972k total, 1135388k used, 8994584k free, 19832k buffers Swap: 2097144k total, 0k used, 2097144k free, 56008k cached
- 在启动Tomcat之后:
Mem: 10129972k total, 3468208k used, 6661764k free, 21528k buffers Swap: 2097144k total, 0k used, 2097144k free, 143428k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2257 tomcat 20 0 5991m 1.9g 19m S 352.9 19.2 3:09.64 java
- 在启动负载10分钟后:
Mem: 10129972k total, 6354756k used, 3775216k free, 21960k buffers Swap: 2097144k total, 0k used, 2097144k free, 144016k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2257 tomcat 20 0 6549m 3.3g 10m S 332.1 34.6 16:46.87 java
xms和xmx = 7G(导致Tomcat崩溃):
- 在启动Tomcat之前:
Mem: 10129972k total, 1270348k used, 8859624k free, 98504k buffers Swap: 2097144k total, 0k used, 2097144k free, 74656k cached
- 在启动Tomcat之后:
Mem: 10129972k total, 6415932k used, 3714040k free, 98816k buffers Swap: 2097144k total, 0k used, 2097144k free, 144008k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2310 tomcat 20 0 9.9g 3.5g 10m S 0.3 36.1 3:01.66 java
- 在启动负载10分钟后(就在Tomcat被杀死之前):
Mem: 10129972k total, 9960256k used, 169716k free, 164k buffers Swap: 2097144k total, 2095056k used, 2088k free, 3284k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2310 tomcat 20 0 10.4g 5.3g 776 S 9.8 54.6 14:42.56 java
Java和JVM版本:
Java(TM) SE Runtime Environment (build 1.7.0_21-b11) Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)
Tomcat版本:
6.0.36
Linux服务器:
Red Hat Enterprise Linux Server release 6.4 (Santiago)
所以我的问题是:
- 为什么会出现这个问题?当JVM内存用尽时,为什么没有抛出OutOfMemoryError?为什么直接使用交换空间?
- 为什么
top
RES
显示Java使用了5.3G内存,实际消耗的内存远远超过这个数?
我已经调查和搜索了一段时间,仍然找不到这个问题的根本原因。非常感谢!
为什么会出现这种问题?当JVM内存用完时,为什么没有抛出OutOfMemoryException异常?
问题并不是JVM内存用完了,而是主机操作系统用完了与内存相关的资源,因此采取了极端措施。操作系统无法知道进程(在这种情况下是JVM)在被告知“不行”时有序关闭的能力。它必须强制终止某些进程,否则整个操作系统可能会挂起。
无论如何,您没有看到OutOfMemoryException是因为这不是OutOfMemoryException情况。实际上,JVM已经被操作系统给予了太多的内存,而且无法收回。这是操作系统必须通过强制终止进程来处理的问题。
为什么它直接使用交换空间?
它使用交换空间是因为整个系统的虚拟内存需求超出了物理内存的容量。这是 UNIX / Linux 操作系统的正常行为。
top RES 显示 java 使用了 5.3G 的内存,实际上消耗的内存更多。这些 RES 数字有点误导性。它们所指的是进程当前正在使用的物理内存的数量……不包括与其他进程共享或可共享的内容。VIRT 数字对于你的问题更为相关。它表示你的 JVM 使用了 10.4G 的虚拟内存……这超出了系统可用的物理内存。
正如其他答案所说,你担心没有获得 OOME 是令人担忧的。即使你得到了 OOME,也不明智去对其进行任何操作。OOME 可能会对你的应用程序 / 容器造成不可检测和更难以恢复的副作用。这就是为什么 OOME 是一个错误而不是异常。
建议:
不要尝试使用比实际物理内存显著更多的虚拟内存,尤其是在使用 Java 时。当 JVM 运行完整的垃圾回收时,它会以随机顺序多次访问其大部分 VM 页面。如果您过度分配了内存,这很可能会导致抖动,从而降低整个系统的性能。
增加系统的交换空间。(但这可能不会有帮助……)
不要尝试从 OOME 中恢复。