容器中 Java RAM 的使用:避免记忆力减退的 5 大技巧

位置:首页>文章>详情   分类: Java教程 > 编程技术   阅读(262)   2023-11-18 07:14:57

在本文中,我们想分享 Java 内存管理和容器内部弹性的细节,这些细节乍一看并不明显。

您将在下面找到需要注意的问题列表和即将发布的 JDK 版本中的重要更新,以及针对核心痛点的现有解决方法。我们收集了前 5 个最有趣和最有用的技巧,以提高 Java 应用程序的资源使用效率。

Docker 中的 Java 堆内存限制

目前,社区正在讨论有关在 Docker 容器中运行 Java 应用程序时内存限制确定不正确的问题。

问题在于,如果未明确定义 Xmx 选项,则由于默认的内部垃圾收集 (GC) 人体工程学算法,JVM 将使用主机操作系统所有可用内存的 1/4。如果 JVM 内存使用量增长超过为 Docker 容器定义的 cgroups 限制,这可能会导致内核终止 Java 进程。

为了解决这个问题,最近在 OpenJDK 9 中实现了一项改进:

OpenJDK 9 中添加了第一个实验性更改,因此 JVM 可以理解它在容器中运行并相应地调整内存限制。”来自 Java 9 将在运行 Docker 时调整内存限制 文章

新的 JVM 选项 (-XX:+UseCGroupMemoryLimitForHeap) 根据 cgroup 中定义的内存限制自动为 Java 进程设置 Xmx。

作为在 Java 9 公开发布之前解决此问题的一个很好的解决方法,可以在 JVM 的启动选项中明确指定 Xmx 限制。 官方 OpenJDK 存储库 中有一个公开的拉取请求,要求“根据 docker 内存限制设置更好的默认 Xmx 值的脚本”。

Jelastic 通过结合使用增强的系统容器虚拟化层和 Docker 镜像,设法避免了错误的内存限制确定。之前我们在文章容器中的 Java 和内存限制:LXC、Docker 和 OpenVZ 中解释了它的工作原理。

跟踪本机非堆内存使用情况

在云中运行 Java 应用程序时,注意 Java 进程的本机内存使用情况也很重要,即所谓的堆外内存。它可以用于不同的目的:

  • 垃圾收集器和 JIT 优化正在跟踪对象图的数据并将其存储在本机内存中。此外,自 JDK8 以来,类的名称和字段、方法的字节码、常量池等现在位于元空间中,元空间也存储在 JVM 堆之外。
  • 此外,为了获得高性能,许多 Java 应用程序在本地区域分配内存。使用 java.nio.ByteBuffer 或第 3 方 JNI 库,这些应用程序存储大型、长期存在的缓冲区,这些缓冲区由底层系统的本机 I/O 操作管理。

默认情况下,元空间分配仅受可用本机操作系统内存量的限制。结合 Docker 容器中不正确的内存限制确定,这会增加应用程序不稳定的风险。限制元数据的大小很重要,特别是当您遇到 OOM 问题时。使用特殊选项 -XX:MetaspaceSize 执行此操作。

由于所有对象都存储在正常的垃圾回收堆内存之外,因此它们对 Java 应用程序的内存占用量的影响并不明显。有一篇很好的文章详细解释了这个问题,并提供了一些如何分析本机内存使用情况的指南:

“几周前,我在尝试分析在 Docker 下运行的 Java 应用程序(Spring Boot + Infinispan)中的内存消耗时遇到了一个有趣的问题。 Xmx 参数设置为 256m,但 Docker 监控工具显示已用内存几乎是两倍。” –分析 Docker 容器中的 java 内存使用情况

以及作者的有趣总结:

“我能说什么作为总结?好吧……永远不要把“java”和“micro”放在同一个句子中,我在开玩笑——请记住,在 java、linux 和 docker 的情况下处理内存比一开始看起来要棘手一些。”

为了跟踪本机内存分配,可以使用特定的 JVM 选项 (-XX:NativeMemoryTracking=summary)。请注意,如果您启用此选项,您将获得 5-10% 的性能损失。

在运行时调整 JVM 内存使用量

另一个减少 Java 应用程序内存消耗的有用解决方案是在 java 进程运行时动态调整 JVM 可管理选项。自 JDK7u60 和 JDK8u20 以来,选项 MinHeapFreeRatioMaxHeapFreeRatio 变得易于管理,这意味着我们可以在运行时更改它们的值,而无需重新启动 Java 进程。

Runtime Committed Heap Resizing 一文中,作者描述了如何通过调整这些可管理的选项来减少内存使用:

“……调整大小又成功了一次,堆容量从 159MB 增加到 444MB。我们描述了至少 85% 的堆容量应该是空闲的,这指示 JVM 调整堆的大小以获得最多 15% 的使用率。”

这种方法可以为可变工作负载带来显着的资源使用优化。改进 JVM 内存大小的下一步可以是允许在运行时模式下更改 Xmx,而无需重新启动 Java 进程。

改进内存压缩

在许多情况下,客户希望尽量减少 Java 应用程序中使用的内存量,从而导致更频繁的 GC。例如,它可以通过在开发、测试和构建环境以及负载高峰后的生产环境中更有效地利用资源来帮助节省资金。然而,根据官方增强票据,当前的 GC 算法需要多个完整的垃圾收集周期来释放所有空闲未使用的内存。

因此,引入了一个新的 JVM 选项 (-XX:+ShrinkHeapInSteps) 来规范 JDK9 中的 GC 算法行为。此设置应更改为 -XX:-ShrinkHeapInSteps 以禁用 4 个完整的 GC 周期。这将更快地释放未使用的 RAM 资源,并最大限度地减少具有可变负载的应用程序中的 Java 堆大小使用。

减少内存使用以加速实时迁移

内存消耗大的 Java 应用程序的实时迁移需要大量时间。为了减少总迁移时间和资源开销,迁移引擎应尽量减少主机之间传输的数据。这可以通过在实时迁移过程之前借助完整的 GC 周期压缩 RAM 来完成。对于各种应用程序来说,这种方法可以更经济高效地克服 GC 周期期间的性能下降,而不是使用未打包的 RAM 进行迁移。

我们找到了与此主题相关的出色研究工作:GC-assisted JVM Live Migration for Java Server Applications作者将 JVM 与 CRIU(Checkpoint/Restore In Userspace)相结合,并引入了一种新的 GC 逻辑,以减少 Java 应用程序从一台主机实时迁移到另一台主机的时间。所提供的方法允许在拍摄 Java 进程状态快照之前启用迁移感知垃圾收集,然后通过在磁盘上设置检查点来冻结正在运行的容器,然后从冻结点恢复容器。

此外,Docker 社区正在整合 CRIU 成为主流。目前此功能仍处于试验阶段。

两者(Java 和 CRIU)的结合可以释放尚未发现的性能和部署优化机会,以改进云中的 Java 应用程序托管。您可以在文章“容器实时迁移:幕后”中找到容器实时迁移如何在云中工作的更多细节。

Java 很棒并且已经在云中很好地工作,特别是在容器中,但我们相信它可以做得更好。因此,在本文中,我们介绍了一组当前问题,这些问题已经可以改进以平稳高效地运行 Java 应用程序。

在 Jelastic,我们在全球数百个数据中心运行着数千个 Java 容器。良好的内存管理对我们至关重要。这就是为什么我们不断将有关 Java 内存的新发现整合到我们的平台中,因此开发人员不必明确处理这些问题。 在 Jelastic 增强平台上试验运行您的 Java 容器

标签2: Java教程
地址:https://www.cundage.com/article/jcg-java-ram-usage-containers-top-5-tips-not-lose-memory.html

相关阅读

Java HashSet 教程展示了如何使用 Java HashSet 集合。 Java哈希集 HashSet 是一个不包含重复元素的集合。此类为基本操作(添加、删除、包含和大小)提供恒定时间性...
SpringApplicationBuilder 教程展示了如何使用 SpringApplicationBuilder 创建一个简单的 Spring Boot 应用程序。 春天 是用于创建企业应...
通道是继 buffers 之后 java.nio 的第二个主要新增内容,我们在之前的教程中已经详细了解了这一点。通道提供与 I/O 服务的直接连接。 通道是一种在字节缓冲区和通道另一端的实体(通...
课程大纲 Elasticsearch 是一个基于 Lucene 的搜索引擎。它提供了一个分布式的、支持多租户的全文搜索引擎,带有 HTTP Web 界面和无模式的 JSON 文档。 Elasti...
解析器是强大的工具,使用 ANTLR 可以编写可用于多种不同语言的各种解析器。 在这个完整的教程中,我们将: 解释基础:什么是解析器,它可以用来做什么 查看如何设置 ANTLR 以便在 Java...
Java 是用于开发各种桌面应用程序、Web 应用程序和移动应用程序的最流行的编程语言之一。以下文章将帮助您快速熟悉 Java 语言,并迈向 API 和云开发等更复杂的概念。 1. Java语言...
Java中的继承是指子类继承或获取父类的所有非私有属性和行为的能力。继承是面向对象编程的四大支柱之一,用于提高层次结构中类之间的代码可重用性。 在本教程中,我们将了解 Java 支持的继承类型,...
Java Message Service 是一种支持正式通信的 API,称为 网络上计算机之间的消息传递。 JMS 为支持 Java 程序的标准消息协议和消息服务提供了一个通用接口。 JMS 提...
Java 项目中的一项常见任务是将日期格式化或解析为字符串,反之亦然。解析日期意味着你有一个代表日期的字符串,例如“2017-08-3”,你想把它转换成一个代表 Java 中日期的对象,例如Ja...
之前,我介绍了spring 3 + hibernate 集成 示例和struts 2 hello world 示例。在本教程中,我将讨论在将 spring 框架与 struts 与 hibern...