You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 21对比Java 8内存占用过高问题排查求助

Java 8升级至Java 21后容器内存占用显著增加的诊断思路

问题背景

将Java应用从Java 8升级至Java 21后,发现内存消耗显著增加。应用部署在基于Alpine OS的容器中,使用Eclipse Temurin Build版本。

问题复现

创建了可复现问题的小型项目,当堆参数设置为-Xms256m -Xmx512m且容器内存限制为1G时,docker stats显示Java进程内存占用从231 MiB跃升至314 MiB,增加了三分之一。

Docker Stats Output

初步分析

启用Native Memory Tracking (NMT)对比Java 8和Java 21的内存占用:

Java 8 NMT输出

Native Memory Tracking:

Total: reserved=1929874KB, committed=393190KB
-                 Java Heap (reserved=524288KB, committed=262208KB)
                            (mmap: reserved=524288KB, committed=262208KB) 
 
-                     Class (reserved=1099014KB, committed=57566KB)
                            (classes #10367)
                            (malloc=1286KB #10537) 
                            (mmap: reserved=1097728KB, committed=56280KB) 
 
-                    Thread (reserved=31968KB, committed=31968KB)
                            (thread #32)
                            (stack: reserved=31829KB, committed=31829KB)
                            (malloc=106KB #186) 
                            (arena=32KB #58)
 
-                      Code (reserved=253118KB, committed=20810KB)
                            (malloc=3518KB #5282) 
                            (mmap: reserved=249600KB, committed=17292KB) 
 
-                        GC (reserved=1738KB, committed=890KB)
                            (malloc=26KB #174) 
                            (mmap: reserved=1712KB, committed=864KB) 
 
-                  Compiler (reserved=159KB, committed=159KB)
                            (malloc=24KB #469) 
                            (arena=135KB #7)
 
-                  Internal (reserved=1844KB, committed=1844KB)
                            (malloc=1812KB #12019) 
                            (mmap: reserved=32KB, committed=32KB) 
 
-                    Symbol (reserved=15183KB, committed=15183KB)
                            (malloc=11915KB #122017) 
                            (arena=3268KB #1)
 
-    Native Memory Tracking (reserved=2366KB, committed=2366KB)
                            (malloc=7KB #90) 
                            (tracking overhead=2359KB)
 
-               Arena Chunk (reserved=196KB, committed=196KB)
                            (malloc=196KB) 

Java 21 NMT输出

Native Memory Tracking:

Total: reserved=2044186KB, committed=424038KB
       malloc: 42596KB #337605
       mmap:   reserved=2001590KB, committed=381442KB

-                 Java Heap (reserved=524288KB, committed=262208KB)
                            (mmap: reserved=524288KB, committed=262208KB) 
 
-                     Class (reserved=1050068KB, committed=13012KB)
                            (classes #17813)
                            (  instance classes #16553, array classes #1260)
                            (malloc=1492KB #45190) (peak=1496KB #45058) 
                            (mmap: reserved=1048576KB, committed=11520KB) 
                            (  Metadata:   )
                            (    reserved=131072KB, committed=69376KB)
                            (    used=68992KB)
                            (    waste=384KB =0.55%)
                            (  Class space:)
                            (    reserved=1048576KB, committed=11520KB)
                            (    used=11130KB)
                            (    waste=390KB =3.38%)
 
-                    Thread (reserved=31927KB, committed=2831KB)
                            (thread #32)
                            (stack: reserved=31826KB, committed=2730KB)
                            (malloc=66KB #191) (peak=79KB #207) 
                            (arena=34KB #60) (peak=2322KB #38)
 
-                      Code (reserved=249888KB, committed=24908KB)
                            (malloc=2200KB #9067) (at peak) 
                            (mmap: reserved=247688KB, committed=22708KB) 
 
-                        GC (reserved=1734KB, committed=882KB)
                            (malloc=22KB #80) (peak=250KB #137) 
                            (mmap: reserved=1712KB, committed=860KB) 
 
-                  Compiler (reserved=238KB, committed=238KB)
                            (malloc=74KB #835) (peak=117KB #857) 
                            (arena=164KB #4) (peak=53061KB #30)
 
-                  Internal (reserved=863KB, committed=863KB)
                            (malloc=827KB #25496) (peak=835KB #25298) 
                            (mmap: reserved=36KB, committed=36KB) 
 
-                     Other (reserved=24KB, committed=24KB)
                            (malloc=24KB #3) (peak=58KB #5) 
 
-                    Symbol (reserved=30116KB, committed=30116KB)
                            (malloc=28254KB #234525) (at peak) 
                            (arena=1862KB #1) (at peak)
 
-    Native Memory Tracking (reserved=5362KB, committed=5362KB)
                            (malloc=87KB #1569) (peak=87KB #1568) 
                            (tracking overhead=5275KB)
 
-        Shared class space (reserved=16384KB, committed=11996KB, readonly=0KB)
                            (mmap: reserved=16384KB, committed=11996KB) 
 
-               Arena Chunk (reserved=2KB, committed=2KB)
                            (malloc=2KB #116) (peak=55748KB #1365) 
 
-                    Module (reserved=127KB, committed=127KB)
                            (malloc=127KB #3158) (at peak) 
 
-                 Safepoint (reserved=8KB, committed=8KB)
                            (mmap: reserved=8KB, committed=8KB) 
 
-           Synchronization (reserved=1751KB, committed=1751KB)
                            (malloc=1751KB #17227) (at peak) 
 
-            Serviceability (reserved=17KB, committed=17KB)
                            (malloc=17KB #9) (peak=17KB #11) 
 
-                 Metaspace (reserved=131387KB, committed=69691KB)
                            (malloc=315KB #118) (at peak) 
                            (mmap: reserved=131072KB, committed=69376KB) 
 
-      String Deduplication (reserved=1KB, committed=1KB)
                            (malloc=1KB #8) (at peak) 

内存对比

最初怀疑的两个原因已被排除:

  • 排除Netty的Direct Buffer/ Native Memory占用:使用无Netty的spring-pet-clinic项目测试,仍存在内存上升现象
  • 排除Alpine系统默认内存分配库:在Ubuntu系统测试,同样出现内存增加问题

后续诊断方向

可采取以下额外步骤进一步排查:

  • 对齐JVM默认参数测试:Java 8到Java 21的JVM默认参数有诸多调整,比如GC算法、元空间设置、新增特性开关等。将Java 21的参数手动对齐到Java 8的默认值,逐一测试哪些参数导致内存上升:

    • 切换GC算法:Java 21默认用ZGC(64位系统),可切回Parallel GC(-XX:+UseParallelGC)观察变化
    • 固定元空间大小:设置-XX:MetaspaceSize和-XX:MaxMetaspaceSize与Java 8一致
    • 关闭新增特性:比如字符串去重(-XX:-UseStringDeduplication)、共享类空间(-XX:-UseSharedSpaces)
  • 深挖NMT差异项:从NMT输出看,Java 21的Symbol区、Metaspace、Synchronization等区域占用明显更高:

    • 用jmap -histo对比Java 8和Java 21的类、字符串常量数量,分析Symbol区翻倍的原因
    • 执行jcmd <pid> VM.metaspace查看元空间的详细使用,对比Class区和Metaspace的实际占用差异
    • 检查Synchronization区域的内存来源,是否有更多锁对象或同步相关资源
  • 容器内存映射分析:在容器内使用pmap <pid>或jcmd <pid> VM.native_memory detail获取细粒度内存映射数据,对比Java 8和Java 21的匿名映射、文件分布差异,定位新增的内存占用区域

  • 更换JDK版本测试:当前使用Eclipse Temurin,可尝试OpenJDK、Azul Zulu等其他厂商的Java 21版本,排查是否是特定构建版本的问题

  • 长期内存趋势监控:观察应用运行数小时后的内存变化,用async-profiler或jprofiler分析内存分配、回收频率,排查是否存在隐性内存泄漏

10月2日更新

使用无依赖的原生Java简易HTTP Server测试,仍能复现内存占用上升现象(Java 21内存略高于Java 8),只是增幅不如Spring Pet Clinic项目明显。

Java简易HTTP Server内存占用


内容的提问来源于stack exchange,提问作者Raghu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 00:02:33