Spring Boot 消耗太多内存

Posted

技术标签:

【中文标题】Spring Boot 消耗太多内存【英文标题】:Spring boot is consuming too much RAM 【发布时间】:2019-01-16 23:45:09 【问题描述】:

我在 Spring Boot 中创建了一些服务,我有 11 个胖 jar,我将它们部署在 docker 容器中,我怀疑每个 jar 都消耗了 1 到 1.5 GB 的 RAM 而没有任何使用,我通过运行检查 RAM :

docker stats containername

一开始我以为是java容器,我尝试改成使用alpine的容器,但没有任何改变,所以我认为唯一的问题是我的jar。有没有办法改变 jar 正在使用的 RAM?或者这种行为是正常的,因为每个 jar 都有一个嵌入的 tomcat?或者也许更好地将一些罐子放在一起并将它们部署为战争并且只使用一个tomcat作为一组“罐子”?有人可以分享他/她的经验吗?,

提前致谢。

【问题讨论】:

您正在运行哪个图像?如果您在本地运行它,您是否尝试过检查消耗的内存? 我正在运行 openjdk:8 并且我尝试使用 openjko:8u171-jdk-alpine3.8 但它没有帮助。让我测试它在本地运行 我在本地测试了它们,同样的事情正在发生 May this article 可以给我们一些关于这个问题的澄清 如果您不告诉您的 JVM 限制其内存使用量,它将使用默认值,这可能非常大。只要有可用的内存,您的垃圾收集器可能就不会做任何事情,即使它做了,它也并不总是返回给系统。这纯粹是Java的工作原理,与Spring boot无关,也不是Spring boot的内存问题。 【参考方案1】:

您可以使用 -e JAVA_OPTS="-Xmx64M -Xms64M" 设置 docker 容器的内存使用情况。

码头文件:

FROM openjdk:8-jre-alpine
VOLUME ./mysql:/var/lib/mysql
ADD /build/libs/application.jar app.jar
ENTRYPOINT exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar

图像运行:

 docker run -d --name container-name -p 9100:9100 -e JAVA_OPTS="-Xmx512M -Xms512M"   imagename:tag

在这里我设置了 512Mb 的内存使用量。您可以设置 1g 或根据您的要求。运行后使用此检查您的内存使用情况。最大为 512Mb。

【讨论】:

谢谢,这解决了我的问题,唯一的问题是我怎么知道我的 jar 需要多少 RAM? @AlanGaytan 通过监控(例如使用 jvisualvm)。您可以看到您的应用程序使用了多少内存,并且可以使用它来手动触发 GC。在正常加载期间执行 GC 后查看您的内存,这是您的应用程序至少需要运行的内容。确保您允许它使用比这更多的内存。太少,要么内存不足,要么不断 GC,导致性能问题。太多,它不会很快 GC,导致你使用过多的内存。【参考方案2】:

这就是 Java 的一般行为方式。 JVM 会占用您给它的内存,它会执行一个名为垃圾收集 (What is the garbage collector in Java) 的进程,以便在它决定应该这样做时释放空间。

但是,如果您不告诉您的 JVM 它可以使用多少内存,它将使用系统默认值,这取决于您的系统内存和您拥有的内核数量。您可以使用以下命令 (How is the default Java heap size determined) 验证这一点:

java -XX:+PrintFlagsFinal -version | grep HeapSize

在我的机器上,初始堆内存为 256MiB,最大堆大小为 4GiB。但是,这并不意味着您的应用程序需要它。

测量内存的一个好方法是使用 jvisualvm 之类的监控工具。此外,您还可以使用执行器的/health 端点来查看堆内存使用情况。

您的堆内存使用通常呈锯齿状 (Why a sawtooth shaped graph),其中内存逐渐被使用,并最终被垃圾收集器释放。

垃圾回收后剩余的内存通常是无法销毁的对象,因为它们仍在使用中。您可以将其视为您的工作记忆。现在,要配置您的-Xmx,您必须在试用后查看您的应用程序的行为:

将其配置为低于您的正常内存使用量,您的应用程序将耗尽内存,并抛出 OutOfMemoryError。 将其配置得太低,但高于您的最低内存使用量,您将看到巨大的性能损失,因为垃圾收集器必须不断释放内存。 将其配置得太高,您将保留在大多数情况下不需要的内存,因此浪费了太多资源。

从上面的屏幕截图中,您可以看到我的应用程序为堆使用保留了大约 1GiB 的内存,而垃圾回收后它只使用了大约 30MiB。这意味着它的-Xmx 值太高了,因此我们可以将其更改为不同的值,然后查看应用程序的行为。

人们通常更喜欢使用 2 的幂(尽管没有限制,如 jvm heap setting pattern 所示)。就我而言,我需要至少 30MiB,因为这是我的应用程序一直使用的内存量。所以这意味着我可以尝试-Xmx32m,看看它的表现如何,并在内存不足或表现更差时进行调整。

【讨论】:

感谢您的解释! 你是救生员!我一直在努力使用我的应用程序,这些应用程序的大小不断增长,阅读了 SO 上的数千篇帖子。您的回答阐明了我的问题,一旦我设置了正确的参数 xms 和 xms,我就解决了我的问题...【参考方案3】:

查看openjkd DockerHub image documentation 后,您似乎可以通过设置-XX:MaxRAM=... 来设置默认堆大小:

Windows Server 容器支持 RAM 限制,但目前是 JVM 无法检测到它。为了防止过多的内存分配, -XX:MaxRAM=... 选项必须指定不大于容器 RAM 限制的值。

来自oracle docs:

默认堆大小 除非在命令行中指定了初始和最大堆大小,否则它们是根据数量计算的 机器上的内存。

【讨论】:

以上是关于Spring Boot 消耗太多内存的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 嵌入式 tomcat 服务器占用 800 MB 内存?

Spring Boot CALL API 消耗

Spring Boot 学习总结(31)—— Spring Native 主要知识点总结

Spring Boot 学习总结(31)—— Spring Native 主要知识点总结

Spring Boot JWT - 如何实现刷新令牌和注销 REST-API

Spring Boot 整合模板引擎 Freemakerthymeleaf