在容器中使用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:五种不丢失内存的方法

相关文章
|
3天前
|
存储 缓存 Java
Java中的缓冲流提升I/O性能,通过内存缓冲区减少对硬件访问
【6月更文挑战第22天】Java中的缓冲流提升I/O性能,通过内存缓冲区减少对硬件访问。`BufferedInputStream`和`BufferedOutputStream`用于字节流,缓存数据批量读写。`BufferedReader`和`BufferedWriter`处理字符流,支持按行操作。使用后务必关闭流。
9 3
|
4天前
|
存储 缓存 Java
并发编程-Java内存模型到底是什么
并发编程-Java内存模型到底是什么
|
2天前
|
缓存 Java 编译器
Java内存模型深度解析
【6月更文挑战第22天】在探索Java内存模型的迷宫中,我们不仅需要理解其结构,还要揭开它运作的神秘面纱。本文将深入挖掘Java内存模型的核心概念,从硬件架构出发,到Java内存模型的设计哲学,再到并发编程中的实际应用,我们将一步步解码Java内存模型的奥秘。
|
1天前
|
存储 Java C++
Java虚拟机(JVM)在执行Java程序时,会将其管理的内存划分为几个不同的区域
【6月更文挑战第24天】Java JVM管理内存分7区:程序计数器记录线程执行位置;虚拟机栈处理方法调用,每个线程有独立栈;本地方法栈服务native方法;Java堆存储所有对象实例,垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息;运行时常量池存储常量;直接内存不属于JVM规范,通过`java.nio`手动管理,不受GC直接影响。
11 5
|
4天前
|
Java 机器人 数据库连接
Java中的内存泄漏问题解析与应对
Java中的内存泄漏问题解析与应对
|
1天前
|
算法 Java
垃圾回收机制(Garbage Collection,GC)是Java语言的一个重要特性,它自动管理程序运行过程中不再使用的内存空间。
【6月更文挑战第24天】Java的GC自动回收不再使用的内存,关注堆中的对象。通过标记-清除、复制、压缩和分代等算法识别无用对象。GC分为Minor、Major和Full类型,针对年轻代、老年代或整个堆进行回收。性能优化涉及算法选择和参数调整。
12 3
|
1天前
|
存储 Java C++
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据,如局部变量和操作数;本地方法栈支持native方法;堆存放所有线程的对象实例,由垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息和常量;运行时常量池是方法区一部分,保存符号引用和常量;直接内存非JVM规范定义,手动管理,通过Buffer类使用。Java 8后,永久代被元空间取代,G1成为默认GC。
9 2
|
5天前
|
安全 Java 开发者
深入理解Java内存模型(JMM)及其对并发编程的影响
【6月更文挑战第19天】在Java的世界中,内存模型是构建高效、线程安全应用的基石。本文将通过探讨Java内存模型(JMM)的核心概念和原理,揭示它如何影响并发编程实践。我们将从JMM的基本定义出发,逐步解析它在同步机制、可见性规则以及有序性保证方面的作用。同时,我们也将讨论JMM对现代Java开发中常见的并发模式和框架的影响。最后,文章会提供一些实际的编码建议和最佳实践,帮助开发者更好地利用JMM来设计并发程序。
|
15小时前
|
Java
使用JProfile查看java导出内存快照
使用JProfile查看java导出内存快照
5 0
|
3天前
|
存储 C语言
C语言---求一个整数存储在内存中的二进制中1的个数--3种方法
C语言---求一个整数存储在内存中的二进制中1的个数--3种方法