Tomcat进程在交换空间用完后被Linux内核杀死;没有收到任何 JVM OutOfMemory 错误

2022-09-04 04:02:10

我正在对tomcat服务器执行负载测试。服务器具有 10G 物理内存和 2G 交换空间。堆大小(xms和xmx)之前设置为3G,服务器工作正常。由于我仍然看到剩余的可用内存很多,并且性能不佳,因此我将堆大小增加到7G并再次运行负载测试。这一次,我观察到物理内存很快就被消耗掉了,系统开始消耗交换空间。后来,tomcat在交换空间用完后崩溃了。我在启动tomcat时进行了包含,但我没有得到任何堆转储。当我检查时,我看到.-XX:+HeapDumpOnOutOfMemoryError/var/log/messageskernel: Out of memory: Kill process 2259 (java) score 634 or sacrifice child

为了提供更多信息,以下是我在堆大小设置为3G和7G时从Linux命令中看到的内容top

xms&xmx = 3G(工作正常):

  • 在开始tomcat之前:

    Mem:  10129972k total,  1135388k used,  8994584k free,    19832k buffers
    Swap:  2097144k total,        0k used,  2097144k free,    56008k cached
    
  • 启动雄猫后:

    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
    
  • 启动雄猫后:

    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分钟后(就在雄猫被杀之前):

    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)

雄猫版:

6.0.36

Linux Server:

Red Hat Enterprise Linux Server release 6.4 (Santiago)

所以我的问题是:

  1. 为什么会出现此问题?当 JVM 内存不足时,为什么没有抛出内存错误?为什么它直接使用交换?
  2. 为什么显示Java使用5.3G内存,消耗的内存要多得多?topRES

我一直在调查和搜索一段时间,仍然找不到此问题的根本原因。多谢!


答案 1

为什么会出现此问题?当JVM内存不足时,为什么没有OutOfMemoryException被抛出?

不是 JVM 内存不足。主机操作系统已耗尽与内存相关的资源,并且正在采取激烈的行动。操作系统无法知道进程(在本例中为 JVM)在响应更多内存的请求时被告知“否”时能够以有序的方式关闭。它必须硬杀一些东西,否则就存在整个操作系统挂起的严重风险。

无论如何,您没有看到OOME的原因是这不是OOME的情况。实际上,JVM已经作系统赋予了太多的内存,没有办法收回它。这就是操作系统必须通过硬杀伤进程来处理的问题。

为什么它直接使用交换?

它使用交换,因为整个系统的总虚拟内存需求不适合物理内存。这是 UNIX/Linux 操作系统的正常行为。

为什么顶级RES显示java正在使用5.3G内存,消耗的内存要多得多

RES数字可能有点误导。它们指的是进程当前使用的物理内存量...排除与其他进程共享或共享的内容。VIRT 编号与您的问题更相关。它说你的JVM正在使用10.4g的虚拟...这比系统上的可用物理内存更多


正如另一个答案所说,令人担忧的是,你没有得到OOME。即使你得到了一个,用它做任何事情都是不明智的。OOME 可能会对您的应用程序/容器造成难以检测和恢复的附带损害。这就是为什么OOME不是.ErrorException


建议:

  • 不要尝试使用比物理内存多得多的虚拟内存,尤其是在使用 Java 时。当 JVM 运行完全垃圾回收时,它将以随机顺序多次接触其大部分 VM 页面。如果您过度分配了内存,则容易导致抖动,从而扼杀整个系统的性能。

  • 请增加系统的交换空间。(但这可能无济于事...)

  • 不要尝试从 OOME 中恢复。


答案 2

您可能在同一台计算机上也有使用内存的其他进程。看起来你的java进程在机器拼命耗尽RAM和交换之前达到了5.3GB左右。(其他进程可能使用12GB-5.3GB = 6.7GB)因此,您的Linux内核牺牲了您的Java进程来保持其他进程的运行。永远不会达到java内存限制,因此您不会获得OutOfMemoryException。

考虑您需要在整个计算机上运行的所有进程,并相应地调整Xmx设置(足以为所有其他进程留出空间)。也许是5gb?

无论如何,计算正在交付的OutOfMemoryExceptions是一种非常糟糕的代码气味。如果我没记错的话,即使只有一个 OutOfMemoryException 也会使 JVM 处于“全押注关闭”状态,并且可能应该重新启动以免变得不稳定。


推荐