Java 8 到 Java 17 的迁移导致 Docker 容器内存显着增加

Roh*_*rya 7 garbage-collection heap-memory g1gc java-8 java-17

您能否帮助我们解答有关 ECS 中 Docker 容器中运行的 Java 8 Spring boot 应用程序的疑问?迁移到 Java 17 后,我们观察到与在 Java 8 上运行的容器的内存使用量相比,容器的内存使用量显着增加了 1GB(我们使用docker stats两个容器的命令比较了内存​​)。我们只有 Java 进程在容器内运行。jmap下面提供了 Java 8 和 Java 17 的命令输出。我们观察到的唯一重大变化是:

  1. 垃圾收集算法是 G1,与 Java 8 中的 CMS 相比,它现在是 Java 17 中的默认算法。
  2. 从 Java 8 中的 166.375MB增加到MaxNewSizeJava 17 中的 1228.0MB。

我们想知道这是否是 Docker 容器中内存使用量增加的原因。我们了解到,新的 G1 垃圾收集算法的内存使用量略高(从命令输出中发现,大约 120 MB,而 CMS 的内存使用量为 12 MB jcmd VM.native_memory),但我们没想到会出现如此剧烈的增加。关于这个问题的任何建议或见解将不胜感激。

Java 8 的 jmap 输出

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 174456832 (166.375MB)
   MaxNewSize               = 174456832 (166.375MB)
   OldSize                  = 1973026816 (1881.625MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 157024256 (149.75MB)
   used     = 135629568 (129.346435546875MB)
   free     = 21394688 (20.403564453125MB)
   86.37491522328881% used
Eden Space:
   capacity = 139591680 (133.125MB)
   used     = 131489752 (125.39839935302734MB)
   free     = 8101928 (7.726600646972656MB)
   94.19598073466842% used
From Space:
   capacity = 17432576 (16.625MB)
   used     = 4139816 (3.9480361938476562MB)
   free     = 13292760 (12.676963806152344MB)
   23.747586128406954% used
To Space:
   capacity = 17432576 (16.625MB)
   used     = 0 (0.0MB)
   free     = 17432576 (16.625MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 1973026816 (1881.625MB)
   used     = 73134816 (69.74679565429688MB)
   free     = 1899892000 (1811.8782043457031MB)
   3.706731981893144% used

38981 interned Strings occupying 3969216 bytes.
Run Code Online (Sandbox Code Playgroud)

Java 17 的 jmap 输出

using thread-local object allocation.
Garbage-First (G1) GC with 2 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 1363144 (1.2999954223632812MB)
   MaxNewSize               = 1287651328 (1228.0MB)
   OldSize                  = 5452592 (5.1999969482421875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 1048576 (1.0MB)

Heap Usage:
G1 Heap:
   regions  = 2048
   capacity = 2147483648 (2048.0MB)
   used     = 1115684864 (1064.0MB)
   free     = 1031798784 (984.0MB)
   51.953125% used
G1 Young Generation:
Eden Space:
   regions  = 977
   capacity = 1312817152 (1252.0MB)
   used     = 1024458752 (977.0MB)
   free     = 288358400 (275.0MB)
   78.03514376996804% used
Survivor Space:
   regions  = 38
   capacity = 39845888 (38.0MB)
   used     = 39845888 (38.0MB)
   free     = 0 (0.0MB)
   100.0% used
G1 Old Generation:
   regions  = 50
   capacity = 794820608 (758.0MB)
   used     = 50331648 (48.0MB)
   free     = 744488960 (710.0MB)
   6.332453825857519% used

43272 interned Strings occupying 4300576 bytes. 
Run Code Online (Sandbox Code Playgroud)

用于 Java 8 的 JVM 选项:

-server -Xms2048m -Xmx2048m -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails
Run Code Online (Sandbox Code Playgroud)

Java 17 的 JVM 选项:

-server -Xms2048m -Xmx2048m -XX:+UseG1GC -Xlog:gc*
Run Code Online (Sandbox Code Playgroud)

use*_*047 4

我们面临着同样的问题。从 JDK11 切换到 JDK 17 后,Docker 镜像的内存消耗增加了一倍。

我们的“旧”JDK 11 与 OpenJ9 VM 一起使用

FROM adoptopenjdk/openjdk12-openj9

我们切换到

FROM eclipse-temurin:17-jdk

Eclipse-Temurin 使用热点 vm 而不是 openj9。切换到带有 OpenJ9 的 JDK 17 后,问题就消失了。

我们现在使用

FROM ibm-semeru-runtimes:open-17-jdk-focal (默认具有 OpenJ9 VM)

它的工作原理与预期一致。