Java ZGC垃圾收集器使用了大量的内存。

14

我使用Springboot构建了一个简单的应用程序。当我在Linux服务器上部署时,我使用的ZGC垃圾回收器会使用很多内存。尽管我尝试将最大堆内存限制为500MB(使用Xmx500m),但JAVA程序仍然使用超过1GB的内存。而当我使用G1回收器时,只使用了350MB。我不知道原因是什么,是JDK11的BUG吗?还是我的启动参数存在问题?

####运行环境
  • 操作系统: CentOS Linux release 7.8.2003
  • JDK版本: jdk11
  • springboot版本: v2.3.0.RELEASE 这是我的Java启动命令
java -Xms128m -Xmx500m \
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC \
-jar app.jar

以下是运行时内存使用情况的截图

堆内存使用情况 https://github.com/JoyfulAndSpeedyMan/assets/blob/master/2020-07-13%20201259.png?raw=true

系统内存使用情况 https://github.com/JoyfulAndSpeedyMan/assets/blob/master/2020-07-13%20201357.png?raw=true


以下是使用默认垃圾回收器时的情况 Java启动命令

java -Xms128m -Xmx500m \
-jar app.jar

堆内存使用情况 https://github.com/JoyfulAndSpeedyMan/assets/blob/master/2020-07-13%20202442.png?raw=true

系统内存使用情况 https://github.com/JoyfulAndSpeedyMan/assets/blob/master/2020-07-13%20202421.png?raw=true

JDK11 默认使用 G1 垃圾回收器。理论上,G1 比 ZGC 更需要占用内存,为什么我没有使用它呢?是我的误解吗?由于我是 JVM 的初学者,所以不明白原因。

4个回答

39
ZGC使用一种称为“有色指针”的技术。其思想是将64位指针中的一些空闲位用于嵌入式元数据。然而,在解引用这些指针时,需要屏蔽这些位,这意味着JVM需要进行额外的工作。
为了避免屏蔽指针的开销,ZGC采用了“多映射”技术。多映射是指将多个虚拟内存范围映射到同一个物理内存范围。
ZGC使用3个Java堆的视图(“marked0”,“marked1”,“remapped”),即3种不同的堆指针颜色和3个相同堆的虚拟内存映射。
因此,操作系统可能报告3倍的内存使用量。例如,对于512 MB的堆,所报告的已提交内存可能高达1.5 GB,不包括堆以外的内存。注意:多映射影响报告的已使用内存,但在物理上,堆仍将使用512 MB的RAM。这有时会导致进程的RSS看起来大于实际物理RAM的数量
另请参见:

我理解的是,只有在容器内运行应用程序时使用ZGC才会发生这种情况,对吗?https://youtu.be/MU8NapbG1IQ?t=2806 - Juraj Martinka
2
@JurajMartinka 不仅仅是在容器中出现的问题。容器的问题在于,一旦进程达到容器配额,操作系统就会杀死该进程。 - apangin
2
请参阅 https://mail.openjdk.java.net/pipermail/zgc-dev/2018-November/000540.html 以获取相关讨论。 - Gili

2

JVM使用的不仅仅是堆内存 - 阅读这篇优秀的答案,以更好地了解JVM内存消耗:Java using much more memory than heap size (or size correctly Docker memory limit)

您需要超越堆检查,并使用像原生内存跟踪这样的工具来获得更清晰的图片。

我不知道您的应用程序有何特殊问题,但ZGC经常被提到适用于大堆。它也是一个全新的收集器,并最近进行了许多更改 - 如果您想使用它,请升级到JDK 14(请参见此处的“更改日志”:https://wiki.openjdk.java.net/display/zgc/Main


0

JVM使用的操作系统内存量(即“已提交堆”)取决于GC运行的频率(以及应用程序开始使用更少的内存时是否取消未使用的内存),这是一个可调整的选项。不幸的是,ZGC默认情况下(目前)没有G1那么积极,但两者都有一些可以尝试的调整选项

P.S. 正如其他人所指出的,htop列中的RES是误导性的,但VisualVM图表显示了真实情况。


-1

这是吞吐量-延迟-占用空间三者之间的权衡结果。在选择这三个方面时,你只能选择其中两个。

ZGC是具有低暂停时间的并发GC。由于你不想放弃吞吐量,所以你会为了占用空间而牺牲延迟和吞吐量。因此,这种高内存消耗并不令人意外。

G1不是低暂停收集器,因此你会将这种权衡转向占用空间,并获得更长的暂停时间,但也赢得了一些内存。


2
操作系统报告的内存使用情况并不反映JVM使用ZGC实际使用的RAM数量(显示的虚拟内存大小的三分之一左右)。请参见上面@apangin的答案。 - BotOfWar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接