如何防止 Spring Boot / Tomcat (Java8) 进程被 OOM 杀死?

Posted

技术标签:

【中文标题】如何防止 Spring Boot / Tomcat (Java8) 进程被 OOM 杀死?【英文标题】:How to prevent a Spring Boot / Tomcat (Java8) process be OOM-killed? 【发布时间】:2018-07-13 17:57:29 【问题描述】:

自从迁移到 Tomcat8/Java8 后,Tomcat 服务器有时会被 OOM 杀死。 OOM = Linux 内核的内存不足终止。

如何防止 Tomcat 服务器被 OOM 杀死?

这可能是内存泄漏的结果吗?我想我会收到一条正常的内存不足消息,但没有OOM-kill。正确的?

我应该更改 HEAP 大小的设置吗? 我应该更改 MetaSpace 大小的设置吗?

知道哪个 Tomcat 进程被杀死,如何检索信息以便我可以重新配置 Tomcat 服务器?

【问题讨论】:

【参考方案1】:

首先检查 oomkill 没有被系统中的另一个进程触发,或者服务器没有被其他进程超载。当其他一些贪婪进程是罪魁祸首时,可能是 Tomcat 被 oomkill 不公平地针对。

堆的最大尺寸 (-Xmx) 应设置为小于服务器上的物理 RAM。如果超过这个,那么分页会导致垃圾收集时性能极差。

如果它是由元空间以无限方式增长引起的,那么您需要找出发生这种情况的原因。一旦达到设置的限制,简单地设置元空间的最大大小将导致内存不足错误。提高限制是没有意义的,因为最终你会达到你设置的任何更高的限制。

运行您的应用程序并它崩溃之前(当然不容易,但您需要判断它),kill -3 tomcat 进程。然后分析堆并尝试找出元空间变大的原因。它通常是由动态加载类引起的。这是您的应用程序正在做的事情吗?更有可能的是,它是一些框架这样做。 (nb oom Killer 会杀死 -9 的 tomcat 进程,之后你将无法进行诊断,所以你需要让应用运行并在此发生之前进行干预)。

也看看这个问题 - 有一个有趣的答案声称对 XML 绑定设置的模糊修复清除了问题(非常有问题但可能值得一试)java8 "java.lang.OutOfMemoryError: Metaspace"

【讨论】:

【参考方案2】:

另一个非常好的解决方案是将您的应用程序转换为 Spring Boot JAR (Docker) 应用程序。通常,此应用程序的内存消耗要少得多。

所以步骤获得了巨大的改进(如果你可以转移到 Spring Boot 应用程序):

迁移到 Spring Boot 应用程序。就我而言,这需要 3 个简单的操作。 使用轻量级基础映像。见下文。 非常重要 - 使用 Java 内存平衡选项。请参阅下面 Dockerfile 的最后一行。这将我正在运行的容器 RAM 使用量从超过 650MB 减少到 ONLY 240MB。运行平稳。所以,在 650MB 上SAVING超过 400MB!!

这是我的 Dockerfile:

FROM openjdk:8-jdk-alpine
ENV JAVA_APP_JAR your.jar
ENV AB_OFF true
EXPOSE 8080
ADD target/$JAVA_APP_JAR /deployments/
CMD ["java","-XX:+UnlockExperimentalVMOptions", "-XX:+UseCGroupMemoryLimitForHeap", "-jar","/deployments/your.jar"]

【讨论】:

以上是关于如何防止 Spring Boot / Tomcat (Java8) 进程被 OOM 杀死?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 如何控制 Tomcat 缓存?

Spring Boot使用嵌入式容器,自定义Filter如何配置?

如何解决tomcat的问题? Spring-Boot“扫描失败”

如何知道spring boot中嵌入了哪个tomcat版本

如何在 Spring Boot 中配置 Tomcat 关闭端口?

spring-boot中如何配置tomcat访问日志的位置和名称?