专栏首页springboot解析10.3.Docker中的Java内存消耗优化以及我们如何使用Spring Boot

10.3.Docker中的Java内存消耗优化以及我们如何使用Spring Boot

如果您的Docker容器占用太多内存而无法达到最佳性能,请阅读下文以了解一个团队如何找到解决方案。

最近,我所在的团队在部署我们的微服务(AWS上Docker中的Java+SpringMVC)时遇到了一个问题。主要问题是,我们的轻量级应用程序占用了太多内存。因此,我们发现了Docker中Java在内存方面的许多棘手之处,并找到了通过重构和迁移到Spring Boot来减少内存消耗的方法。这项工作的结果非常吸引人,我决定与你们分享。

在部署之前,作为具有常识的开发人员,我们能够估计应用程序将消耗多少内存。为此,我们制定了一个清晰易懂的方程式来查找RSS

RSS = Heapsize + MetaSpace + OffHeap size  

这里OffHeap由线程堆栈,缓冲区,库(* .jars)和JVM代码本身组成,在这,我要引用另外一个概念——常驻集。

常驻集

常驻集大小是当前分配给进程并由进程使用的RAM数量。它包括代码、数据和共享库。

让我们根据本地Java VisualVM值找到它:

RSS = 253(Heap) + 100(Metaspace) + 170(OffHeap) + 52*1(Threads) = 600Mb (max avarage)

RSS = 253(堆)+ 100(元空间)+ 170(OffHeap)+ 52 * 1(线程)= 600Mb(最大平均值)

我们得到的结果是:大概600Mb就足够了,我们选择了一个t2.micro AWS实例(带有1Gb RAM)进行部署,开始部署我们的app,首先,我想通过JVM选项提供一些关于内存配置的信息:

-XX:MaxHeapFreeRatio=70 
-XX:CompressedClassSpaceSize=64m 
-XX:ReservedCodeCacheSize=64m 
-XX:MaxMetaspaceSize=256m 
-Xms256m 
-Xmx750m 

此外,作为应用程序的基本图像,我们选择了  jetty:9-alpine,因为我们发现它是Jetty中Java * .wars中最轻量级的图像之一。

正如我所提到的,似乎600Mb就足够了,因此启动了一个具有以下内存限制的容器:

docker run -m 600m

那你觉得怎么样?由于内存不足,我们的容器被DD(Docker守护程序)杀死。这真的很令人惊讶,因为 这个容器已经在本地启动,  具有完全相同的参数(它可以是一个单独的讨论主题)。通过逐步增加容器的内存限制,我们达到了700 ...我在开玩笑,我们得到850Mb。

是真的吗?

经过一些观察和阅读有用的文章后,我们决定进行一些测量。结果非常奇怪和有争议。

  • 堆大小与我们之前(本地)发布的大小相同:
  • 但Docker展示了一些疯狂的统计数据

争议

怎么回事,伙计们?情况变得非常混乱......

我们花了很多时间寻找这些有争议的数字的解释,发现并不是只有我们才有这些问题。在阅读了更多的源代码并使用本机内存跟踪器分析了应用程序之后,我们离答案更近了。我可以总结。大部分额外的内存用于存储已编译的类及其元数据,您可能会问,关于JavaVM/Docker统计数据的争议性数字呢?好问题。事实证明,Java VisualVM对OffHeap关系很微妙,因此,使用这个工具来调查Java应用程序的内存消耗可能非常棘手。此外,了解您使用的JVM选项也非常重要。我发现,

指定-Xmx=512m来给JVM分配一个512mb堆内存,这是一个发现。它没有指定JVM将其整个内存使用限制在512mb,会有代码缓存和各种各样的堆外数据,要指定总内存,应该使用-XX:MaxRAM参数。注意,MaxRam=512m时,堆大小大约为250mb。请注意您的应用程序JVM选项。

NMT和JavaVisualVM Memory Sampler使我们发现内部核心框架被多次复制为内存中的依赖项。并且重复的数量等于我们的微服务中的子模块的数量。为了更好地掌握这一点,我想说明我们的“微服务”结构:

这是来自NMT(在我的本地机器上)的一个模块的快照(具有73MB加载的类元数据,42MB线程和37MB代码,包括libs):

据我们所知,以这种方式构建应用程序是一个很大的错误。首先,每个*.war都被部署为Jettyservlet容器中的一个单独的应用程序,这是非常奇怪的,我同意,因为根据定义,微服务应该是一个部署应用程序(部署单元)。其次,Jetty在内存中分别为每个* .war保存所有必需的lib,即使所有这些库都具有相同的版本。结果,DB连接,来自核心框架的各种基本功能等在内存中被复制。

常识解决方案是重构并使我们的应用程序成为真正的微服务。此外,我们怀疑我们需要一整箱Jetty,我认为,你听到这句名言:

“不要在Jetty中部署应用程序,在应用程序中部署Jetty。”

我们决定尝试使用嵌入式Jetty的Spring Boot,因为它似乎是独立应用程序中最常用的工具,特别是在我们的案例中。几乎没有配置,没有XML,每个Spring Framework优势和很多插件,这些能够自动配置,有大量实用的教程和文章展示了如何在互联网上使用它。

此外,由于我们不再需要单独的Jetty应用程序服务器,因此我们将基本Docker镜像更改为简单的轻量级OpenJDK。

openjdk:8-jre-alpine

然后,我们根据新要求重构了我们的应用程序。在一天结束时,我们得到了类似的东西:

从JavaVirtualVM中进行测量:

做了一些改进后,但与之前版本的应用程序的所有工作和结果相比并没有那么大的差别:

查看Docker的统计数据:

太好了,我们的内存消耗减少了一半。

结论

对我们的团队来说,这是一个有趣的挑战。试图找出事物断裂的根本原因可以让你找到真正好奇的事实,并让你对某个特定领域的视野更深入、更宽广。相信互联网社区,因为我们经常试图解决这些难度类似的问题。另外,不要太过于相信Java VisualVM的内存消耗预算,一定要小心。

在Docker容器中有一个非常好的Java内存使用分析,可以在其中找到关于它如何工作的清晰解释和详细信息。

原文链接:https://blog.csdn.net/g6u8w7p06dco99fq3/article/details/92026713

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 10.1.微服务为什么一定要上Docker?

    早在2013年的时候,docker就已经发行,然而那会还是很少人了解docker。一直到2014年,Martin Fowler提出了微服务的概念,两个不相干的技...

    itjim
  • 10.2.为什么需要Docker?

    估计大家也可能听过Docker这项技术(在论坛上、招聘技能上、交流群上等等),要是不了解Docker,都不好意思在网上冲浪的时候吹牛逼了。

    itjim
  • 1.10.Spring Boot核心注解@Spring常用注解

    @Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)

    itjim
  • Linux 系统运行速度太慢的关键原因,看看这篇

    我们在搞清楚如何加速Linux计算机之前,需要知道哪些方法可以帮助我们找到引导时启动的服务、以更高或更低优先级运行的进程、CPU运行状况、内存是否塞满了过多数据...

    用户6543014
  • Linux 系统运行速度太慢的关键原因全都在这了

    我们在搞清楚如何加速Linux计算机之前,需要知道哪些方法可以帮助我们找到引导时启动的服务、以更高或更低优先级运行的进程、CPU运行状况、内存是否塞满了过多数据...

    用户6543014
  • Linux 系统运行速度太慢的关键原因,看看这篇

    https://mp.weixin.qq.com/s/_OHDxCIWQDEMa3vsEXgVDA

    用户5807183
  • 微服务和传统中间件平台

    微服务与部署在中间件平台(esb、应用服务器)上的传统服务有何不同?什么是微服务体系结构模式,它解决了什么问题?本文将讨论所有这些重要的主题,并描述如何管理、管...

    程序你好
  • Linux内存描述之概述--Linux内存管理(一)

    传统的多核运算是使用SMP(Symmetric Multi-Processor )模式:将多个处理器与一个集中的存储器和I/O总线相连。所有处理器只能访问同一个...

    233333
  • 如何使容器成为架构师最好的朋友

    数字转型正在从根本上改变全球组织的经营方式。通过DevOps实践,IT团队正在帮助降低成本,提高敏捷性,并创建一个创新驱动增长的新时代。但是是什么驱动着DevO...

    CNCF
  • IBM押注AI、量子计算、区块链,发布未来5年五大科技预测

    IBM 发布了包括人工智能机器人显微镜、消除 AI 偏见、量子计算、区块链及加密技术在内的五大科技预测,并认为这五大技术将在未来五年改变人类生活。 AI 科技...

    AI科技评论

扫码关注云+社区

领取腾讯云代金券