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

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

相关文章
|
8月前
|
Java
Java语言实现字母大小写转换的方法
Java提供了多种灵活的方法来处理字符串中的字母大小写转换。根据具体需求,可以选择适合的方法来实现。在大多数情况下,使用 String类或 Character类的方法已经足够。但是,在需要更复杂的逻辑或处理非常规字符集时,可以通过字符流或手动遍历字符串来实现更精细的控制。
506 18
|
8月前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
350 4
|
8月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
472 5
|
9月前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
263 11
|
9月前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
10月前
|
算法 Java
Java语言实现链表反转的方法
这种反转方法不需要使用额外的存储空间,因此空间复杂度为,它只需要遍历一次链表,所以时间复杂度为,其中为链表的长度。这使得这种反转链表的方法既高效又实用。
666 0
|
10月前
|
存储 Java 数据处理
Java映射操作:深入Map.getOrDefault与MapUtils方法
结合 `getOrDefault`方法的简洁性及 `MapUtils`的丰富功能,Java的映射操作变得既灵活又高效。合理地使用这些工具能够显著提高数据处理的速度和质量。开发人员可以根据具体的应用场景选择适宜的方法,以求在性能和可读性之间找到最佳平衡。
338 0
|
10月前
|
缓存 人工智能 NoSQL
Java中实现Token设置过期时间的方法
本文介绍了在Java应用中实现Token设置过期时间的多种方法,包括使用JWT和Redis缓存,并结合定时任务清理过期Token,以提升系统安全性与用户隐私保护。
1135 0
|
10月前
|
算法 Java 开发者
Java 项目实战数字华容道与石头迷阵游戏开发详解及实战方法
本文介绍了使用Java实现数字华容道和石头迷阵游戏的技术方案与应用实例,涵盖GUI界面设计、二维数组操作、游戏逻辑控制及自动解法算法(如A*),适合Java开发者学习游戏开发技巧。
648 46
|
11月前
|
安全 Java API
Java 集合高级应用与实战技巧之高效运用方法及实战案例解析
本课程深入讲解Java集合的高级应用与实战技巧,涵盖Stream API、并行处理、Optional类、现代化Map操作、不可变集合、异步处理及高级排序等核心内容,结合丰富示例,助你掌握Java集合的高效运用,提升代码质量与开发效率。
414 0