我们的一个 sap 系统(PI ABAP+JAVA 堆栈)出现了性能问题。机器配置的整个 64GB 都被占用了(8 个核心也是如此)。每个人都怀疑是 Java 部分的问题,但我的看法不同。
Java 服务器节点因内存不足错误而重新启动。查看 hprof 文件,我发现它们的大小只有 1.2G(3 个服务器节点的平均值),而为服务器节点配置了 3GB(-Xms 和 Xmx)的堆。这一观察导致了以下疑问。
我读到过,当 Xms 和 Xmx 设置为相同值时,jvm 在启动时会分配整个堆。如果是这样,服务器节点从一开始就会有 3GB 的堆。如果是这样,为什么它没有反映在 hprof 文件中,或者如果 hprof 仅包含运行时分配给对象的内存,则大小清楚地表明堆内存是空闲的(超过 50%),那么 OOM 错误是如何发生的...!!..??
我还知道 Linux 会执行一种称为内存过度使用的操作。即,内存实际上不是在请求时分配的,而是在实际使用时分配的。这是否会导致内存不足异常。例如,当 JVM 启动时,操作系统会告诉它已为您分配了 3GB 内存,但实际上会将其推迟到实际需要时。当 JVM 实际尝试将内存分配给对象时,其他一些应用程序可能已经耗尽了内存。这可能吗……??
即使 Java 节点有内存泄漏问题,也不会局限于 3GB 的堆。它怎么会占用整个 64G 的物理内存呢....???
我观察到的另一件事是交换空间仅使用了 50%。
对此有任何了解吗...!
答案1
SAP OSS 也在调查这个问题。今天我收到了他们的回复。我的观察是正确的。Java 不是罪魁祸首。ABAP 堆栈遇到了一些问题,没有释放内存。重新启动 ABAP 工作进程后,内存在操作系统级别得到释放。
但我也想了解问题的突出部分,比如这种情况是否会发生,导致 JAVA OOM 错误......??..!!。任何这方面的信息都会有所帮助。
答案2
在 Linux 上,默认情况下,在启发式模式下启用了过度使用。这意味着内核通常会允许过度使用 - 这意味着它将向所有请求它的进程承诺比它实际可以提供的更多的内存,希望进程永远不会真正开始同时使用所有内存。也许您的服务器上已禁用过度使用,您可以通过运行以下命令进行检查:
$ cat /proc/sys/vm/overcommit_memory
如果值为 0,则开启启发式过度提交。
如果实际内存使用量超过系统可以提供的 RAM 量,内核将激活 OOM 终止程序,它会尝试终止进程以释放内存。它通常会终止消耗大量 RAM 的最年轻进程,但您不能依赖它。它可能会(并且会)造成严重破坏。您可以通过调整 /proc//oom_adj 来修改 OOM 的亲和性以终止特定进程(例如,如果您想避免 OOM 终止数据库或其他一些大 RAM [滥用] 用户的情况)。
因此,如果您的系统进入 OOM 阶段,Java 进程可能会被立即终止 - 这不会在您观察到的 Java 日志中出现“内存不足”消息。
将 Xmx 和 Xms 设置为相同值将阻止堆大小调整,但这并不意味着 Java 进程将在启动时立即开始使用所有内存。它将分配所需的 VIRT 内存,但常驻数据集不会增长到 Xms,而是会保持在所需的最低水平。
就虚拟内存而言:内核将向 Java 进程承诺(过度使用)其要求的内存量(Xmx + 一些额外内存),但不会立即分配所有内存。仅会分配当前数据所需的内存量,您可以通过观察常驻集大小(任务使用的非交换物理内存)来查看分配的内存量。要查看 VIRT 和 RSS 大小,您可以运行以下命令:
$ ps aux | egrep '(^USER|java)'
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
tomcat 10229 21.5 9.1 6813688 548344 ? Sl 09:01 1:10 ....java...
很有可能,您观察到的错误表明在 Java 虚拟机进程下运行的程序缺少堆空间。尝试增加 Xmx 设置并重新测试您的应用程序。