k8s上的Dockerized Spring Boot应用几乎占用了所有可用内存

时间:2018-10-31 10:38:58

标签: docker spring-boot memory-management memory-leaks kubernetes

我有一个简单的Spring Boot应用程序,部署在k8s(2个Pod)上。 简要描述了此应用程序从生产者那里获取消息并将其处理给消费者。没什么复杂的。

UPD:

  • java版本:1.8.172
  • javaMemoryOpts:-Xmx2048m -XX:+ UnlockExperimentalVMOptions -XX:+ UseCGroupMemoryLimitForHeap

memory monitoring 这是2个窗格之一的内存消耗。

  • 蓝线-k8s请求的内存
  • 橙色线-工作集
  • 绿线-服务使用
  • 黄线-内存限制为k8s

问题是尽管服务简单,但内存使用率很高。 我对应用程序进行了概要分析,但是在服务方面似乎还不错:仅有约60个线程,没有内存泄漏等。

已用内存永远不会超过k8s限制,即使它非常接近(没有OOM)。 当然,我可以添加更多的豆荚,消耗量将变得均匀,但是我认为这不是正确的方法。

一件事使我感到困惑,为什么即使在开始时也总是使用超出要求的内存。

实际上,我不知道这是怎么回事。是否有人有想法或知道如何减少应用程序的内存使用?

3 个答案:

答案 0 :(得分:1)

通常,Kubernetes代表请求和限制机制,以便控制资源(CPU,内存)。请求任务旨在为群集Pod中的容器提供足够的资源。限制确保容器永远不会达到特定资源的定义值。浏览并访问以下文章:

调整JVM是一个非常复杂的过程,要获得良好的结果和足够水平的计算系统利用率。我建议查看有关该主题的下一个Web链接:

答案 1 :(得分:0)

mk_sta的答案很有帮助,您所需的所有信息可能都在这些文档中,但是我认为有必要在完整答案中总结要点。

您的-Xmx2048m(2Gb)选项设置了最大堆大小,但是应用程序将使用更多的内存-元空间,垃圾收集器和许多其他开销(这是“堆外”内存)。

无论您的App有多简单,Java都会使用可用的堆大小。一个3行的输出随机字符串的应用程序(如果提供了2Gb的堆)最终将全部使用。因此,如果您的Spring Boot应用程序是“简单的”就没关系-堆将增长直到达到最大值,然后您将看到垃圾收集-这些是绿线中的锯齿状齿。

所以这两件事可能一起解释了为什么您看到内存使用率上限约为3.8Gb。

您正在绘制的图形可能显示了性能良好的应用程序,因此不必担心内存泄漏。从图片中我看不出它们是次要还是主要收藏。也就是说,我无法从图片中推断出缩小Xmx的风险。

尽管您说自己的Spring Boot应用程序是“简单的”,但是没有看到它的pom到底有多复杂,是无法知道的。但是该文件是从此处的mk_ska链接的...

https://github.com/dsyer/spring-boot-memory-blog/blob/master/cf.md

...是一个非常有用的应用程序,因为它为“小型” Spring Boot应用程序显示了一些不错的默认设置-例如,使用Freemarker生成非静态内容的应用程序可以在32Mb的堆中愉快地运行。

因此,简单的答案是在看到垃圾回收崩溃之前,尝试将-Xmx缩小到最小。您也可以将其降低到32Mb。

然后,您可以使用这些发现来为K8S清单中的资源限制和资源请求设置一些合理的值。您将需要比堆大小(例如32Mb)更多的东西-如该文档中所述,Spring启动应用程序比512Mb或1Gb的容器内存更快乐。您可能会摆脱256Mb的限制,但这太紧了。

答案 2 :(得分:0)

我写了一个教程来帮助确定k8s上Spring Boot容器的大小。在设置极限存储值之前,有很多参数需要检查。而且探查很重要。

由于Java内存占用,实际的LTS JVM尚未准备好使用非保证的内存(请求和限制值之间的区域)。 JVM将等待完整的GC才能将未使用的内存释放给系统,因此长时间停留在该区域是有风险的。在这样的GC期间,您的线程将增长,并且Java堆栈内存也将增长。

我在这里逐步详细介绍了:https://loadteststories.com/java-kubernetes-sizing-my-docker-openjdk-spring-boot-microservice/