在容器中使用Java RAM:五种不丢失内存的方法

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 本文讲的是在容器中使用Java RAM:五种不丢失内存的方法【编者的话】在这篇文章中,我们想分享一些看起来不那么明显的关于在容器内部中Java内存管理和弹性扩展的细节。 您将看到在即将发布的JDK版本中需要注意的问题和重要更新的列表,以及核心难点的现有解决方法。
本文讲的是在容器中使用Java RAM:五种不丢失内存的方法【编者的话】在这篇文章中,我们想分享一些看起来不那么明显的关于在容器内部中Java内存管理和弹性扩展的细节。 您将看到在即将发布的JDK版本中需要注意的问题和重要更新的列表,以及核心难点的现有解决方法。 我们收集了可以提高Java应用程序的资源使用效率的五个最有趣和最有用的问题点。

【3 天烧脑式 Docker 训练营 | 上海站】随着Docker技术被越来越多的人所认可,其应用的范围也越来越广泛。本次培训我们理论结合实践,从Docker应该场景、持续部署与交付、如何提升测试效率、存储、网络、监控、安全等角度进行。

在Docker中对Java Heap内存的限制

最近,社区中正在讨论在Docker容器中运行Java程序时内存限制错误的问题。
screen.png

JVM的内存使用超出Docker容器的cgroups限制时被内核杀死。

解决这个问题,其中的一个改善对策已经在OpenJDK 9中实现:

“在OpenJDK 9中已经添加了一个实验性的变更,JVM能够感知到Java程序正运行在一个容器中,因此可以调整内存的限制。” 可以查看文章 Java 9 Will Adjust Memory Limits if Running with Docker 。
一个新的JVM参数(-XX:+UseCGroupMemoryLimitForHeap)将根据cgroup中的内存限制自动的为Java进程设置 Xmx参数。

在Java 9正式发布前,Xmx限制作为JVM的一个具体明确的启动参数,这将作为一个变通的方案来解决这个问题。在 official OpenJDK repo 中已经有一个pull请求 “a script to set better default Xmx values according to the docker memory limits” 。

Jelastic通过使用与Docker镜像相结合的增强型系统容器虚拟化层来忽略错误的内存限制。在之前我们已经在文章《 Java and Memory Limits in Containers: LXC, Docker and OpenVZ 》中解释过它是如何工作的。

跟踪不在堆中的原生内存使用

当在云中运行Java应用时,注意Java进程的原生内存使用情况同样非常重要,被称为off-heap内存。它可以被用做以下不同目的:

垃圾回收器和JIT优化的Object Graphs在原生内存中被跟踪和储存数据。此外,从JDK8开始,类的名称和字段,方法的字节流,常量池等已经被分配到Metaspace中,也是在JVM堆之外的内存区域中。

因此,为了追求高性能的Java应用可以在原生内存区中分配内存。使用java.nio.ByteBuffer或者第三方的库可以使得应用保存大量的在系统原生I/O操作层面被管理的缓冲区。

默认的情况下,Metaspace的分配只受到原生系统内存的限制。在Docker容器的混合体中使用不正确的内存限制,将会增加应用不确定的风险。限制metadata的大小是重要的,尤其是当你存在OOM的问题时。可以使用参数 -XX:MaxMetaspaceSize来进行限制。

将所有对象都存储在普通的垃圾回收堆内存之外,对于Java应用程序的内存占用空间有何影响并不明显。有一篇很好的文章详细解释了这个问题,并提供了一些如何分析本地内存使用的指导原则: 

“几周之前,我在尝试分析一个运行在Docker中的Java应用(Spring Boot + Infinispan)的内存使用情况时,遇到了一个有趣的问题。Xmx参数被设置为256m,但是在Docker的监控工具中至少有两次使用了更多的内存。” —— Analyzing java memory usage in a Docker container 。
在文章的结尾作者较为风趣的说明:

“我可以说什么作为结论? 好吧,从来没有在同一句话中说“java”和“micro”,我在开玩笑 - 只要记住,在java的中处理内存,Linux和Docker要比看起来更棘手一些。“
为了跟踪原生内存的分配,可以使用(-XX:NativeMemoryTracking=summary)的JVM参数。请注意如果设置了这个参数将使你获得5%~10%的性能提升。

在运行时缩减JVM使用的内存

在Java应用中其它有效降低JVM中内存消耗的方法是在运行时调整可管理的参数,从JDK7u60和JDK8u20开始,MinHeapFreeRatio和MaxHeapFreeRatio参数已经变得更加容易的被管理。这意味着我们可以在运行时改天它们的值,而无需重新启动Java进程。

在文章《 Runtime Committed Heap Resizing 》中,作者介绍了如何减少内存使用来调整这些可管理的选项:

“……多次调整内存使用的大小,堆容量从159MB增加到444MB。 描述堆容量值的85%应该是已经被释放的,并且指出JVM调整堆的大小可以获得最多15%的使用率。“
这种方法可以为变量加载带来显着的资源使用优化。 而改进JVM内存大小调整的下一步可以允许在运行时模式下更改Xmx,而不需要重新启动Java进程。

提高内存的压缩率

在多数情况下,客户希望最大程度地减少Java应用程序中使用的内存量,从而可以更频繁的GC。例如,这样可以帮助在开发、测试和编译环境中更高效的利用资源来节省资金,同样在生产环境中也适用。然而,根据官方的增强版本说明,当前的GC算法需要多次完整的GC周期来释放全部未被使用的内存。

作为改进,JDK9中增加了一个新可以控制GC算法的的JVM参数(-XX:+ShrinkHeapInSteps)。这个设置将屏蔽4轮完整的GC循环。这样将更加迅速的释放未被使用的内存同时也可以使应用中的变量加载可以最小的使用Java的堆空间。 

减少内存使用速度以便更快速的流动

实时迁移消耗大量内存的Java应用往往需要大量的时间。为了降低总的迁移时间和资源的消耗,迁移引擎通常使用小规模的数据量在服务器之间进行传输。 可以在实时传输前通过压缩RAM来帮助全部GC循环。对于各种应用来说,这种方法可以更具成本效益,以克服在GC循环期间的性能下降,而不是使用未压缩的RAM进行迁移。

我们发现了与此主题相关的伟大研究工作:  GC-assisted JVM Live Migration for Java Server Applications 。作者将JVM与CRIU(检查点/用户空间中的恢复点)集成,并引入了一个新的GC逻辑,以减少Java应用程序从一个主机迁移到另一个主机的时间。所提供的方法允许在执行Java进程状态的快照之前启用迁移感知垃圾收集,然后通过在磁盘上检查它来冻结运行的容器,然后从冻结的角度恢复容器。

此外,Docker社区将 CRIU 整合为主流。目前这个功能还处于试验阶段。 

两者(Java和CRIU)的结合可以释放尚未发现的性能和部署优化的机会,从而改进云中的Java应用程序托管。您可以在文章“ Containers Live Migration: Behind the Scenes ”中找到有关容器在云中如何运行的更多细节。

Java是伟大的,已经在云中很好地运行,特别是在容器中,但是我们认为它可以更好。因此,在本文中,我们介绍了一系列当前可能已经改进的问题,以便顺利,高效地运行Java应用程序。

在Jelastic,我们在全球数百个数据中心运行了数千个Java容器。良好的内存管理对我们至关重要。这就是为什么我们不断将Java内存使用的提高纳入我们的平台,所以开发人员不必明确地处理这些问题。 在Jelastic增强平台上运行Java容器的实验 。 

原文链接:Java RAM Usage in Containers: Top 5 Tips Not to Lose Your Memory(翻译:李强)

原文发布时间为:2017-05-27

本文作者:李强

本文来自云栖社区合作伙伴Dockerone.io,了解相关信息可以关注Dockerone.io。

原文标题:在容器中使用Java RAM:五种不丢失内存的方法

相关文章
|
1月前
|
Kubernetes 监控 Cloud Native
|
19天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
68 4
|
29天前
|
Java API
Java 对象释放与 finalize 方法
关于 Java 对象释放的疑惑解答,以及 finalize 方法的相关知识。
48 17
|
23天前
|
Java 测试技术 Maven
Java一分钟之-PowerMock:静态方法与私有方法测试
通过本文的详细介绍,您可以使用PowerMock轻松地测试Java代码中的静态方法和私有方法。PowerMock通过扩展Mockito,提供了强大的功能,帮助开发者在复杂的测试场景中保持高效和准确的单元测试。希望本文对您的Java单元测试有所帮助。
64 2
|
1月前
|
机器学习/深度学习 算法 物联网
大模型进阶微调篇(一):以定制化3B模型为例,各种微调方法对比-选LoRA还是PPO,所需显存内存资源为多少?
本文介绍了两种大模型微调方法——LoRA(低秩适应)和PPO(近端策略优化)。LoRA通过引入低秩矩阵微调部分权重,适合资源受限环境,具有资源节省和训练速度快的优势,适用于监督学习和简单交互场景。PPO基于策略优化,适合需要用户交互反馈的场景,能够适应复杂反馈并动态调整策略,适用于强化学习和复杂用户交互。文章还对比了两者的资源消耗和适用数据规模,帮助读者根据具体需求选择最合适的微调策略。
140 5
|
30天前
|
缓存 监控 Java
在使用 Glide 加载 Gif 动画时避免内存泄漏的方法
【10月更文挑战第20天】在使用 Glide 加载 Gif 动画时,避免内存泄漏是非常重要的。通过及时取消加载请求、正确处理生命周期、使用弱引用、清理缓存和避免重复加载等方法,可以有效地避免内存泄漏问题。同时,定期进行监控和检测,确保应用的性能和稳定性。需要在实际开发中不断积累经验,根据具体情况灵活运用这些方法,以保障应用的良好运行。
|
1月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
20 3
|
1月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
19 2
|
1月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
19 1
|
1月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
34 1
下一篇
无影云桌面