我们使用 Java 11.0.11 和 -XX:ReservedCodeCacheSize=375m 运行一个复杂的集群应用程序,并 24/7 全天候附加相同的负载配置文件,无需重新启动。
在我们将 AdoptOpenJDK jdk-11.0.11+9 更新为 Temurin jdk-11.0.13+8 后,两者都使用 -XX:ReservedCodeCacheSize=375m 且我们的应用程序中没有任何代码更改,只是更新了 Java,我们注意到以下更改:
- 在具有 32 个 vCPU、256GB RAM 和 -Xms160G -Xmx160G 的 AWS r5.8xlarge上运行的进程 CPU 使用率从 ~ 30% 增加到 ~ 35% ,这可能是由高 JVM Sweeper 线程活动引起的(见下文)
- JVM CodeHeap 'non-profiled nmethods'(C2 优化代码)已填满(使用 = 176 MB,最大 = 184 MB)与 Java 11.0.11 一样,这是预期的但
- JVM CodeHeap 'profiled nmethods'(C1 优化代码)在进程重新启动后不久被填满(146 MB 已用内存,184 MB 最大内存),并在 15 分钟后减少到 ~ 40 MB,从那时起一直保持在这个水平( 24小时)
Java 11.0.13 + -XX:ReservedCodeCacheSize=375m,时区=UTC+1
对比
Java 11.0.11 + -XX:ReservedCodeCacheSize=375m,时区=UTC+1
下一步是将 -XX:ReservedCodeCacheSize 增加到 512m,但大约 3 小时后,JVM CodeHeap 'profiled nmethods'(C1 优化代码)再次减少到大约 50 MB,甚至随着时间的推移进一步减少
Java 11.0.13 + -XX:ReservedCodeCacheSize=512m,时区=UTC+1
因此,我们决定将 -XX:ReservedCodeCacheSize 加倍至 1024m,并且在过去 7 天中,两个 CodeHeap 区域都保持稳定
- CodeHeap 'non-profiled nmethods':405 MB 已用内存,405 MB 已提交内存,508 MB 最大内存
- CodeHeap 'profiled nmethods':258 MB 已用内存,258 MB 已提交内存,508 MB 最大内存
Java 11.0.13 + -XX:ReservedCodeCacheSize=1024m 稳定运行一周,时区=UTC+1
我们在不同节点和一个节点上尝试了这些更改,结果始终是可重现的。
使用 -XX:ReservedCodeCacheSize=375m,Linux perf 工具显示 Sweeper 线程的 CPU 使用率很高,1024m 时完全消失。
perf record -F 99 --output=perf-375m-uptime-21hours.data -ag -p <PID> sleep 60
+ 4.82% 0.00% Sweeper thread libpthread-2.17.so [.] start_thread
+ 4.82% 0.00% Sweeper thread libjvm.so [.] thread_native_entry
+ 4.82% 0.00% Sweeper thread libjvm.so [.] Thread::call_run
+ 4.82% 0.00% Sweeper thread libjvm.so [.] JavaThread::thread_main_inner
+ 4.82% 0.00% Sweeper thread libjvm.so [.] NMethodSweeper::sweeper_loop
+ 4.82% 0.00% Sweeper thread libjvm.so [.] NMethodSweeper::possibly_sweep
+ 4.79% 0.03% Sweeper thread libjvm.so [.] NMethodSweeper::sweep_code_cache
+ 4.24% 0.03% Sweeper thread libjvm.so [.] NMethodSweeper::process_compiled_method
+ 4.05% 0.00% C2 CompilerThre libpthread-2.17.so [.] start_thread
+ 4.05% 0.00% C2 CompilerThre libjvm.so [.] thread_native_entry
+ 4.05% 0.00% C2 CompilerThre libjvm.so [.] Thread::call_run
+ 4.05% 0.00% C2 CompilerThre libjvm.so [.] JavaThread::thread_main_inner
+ 4.05% 0.00% C2 CompilerThre libjvm.so [.] CompileBroker::compiler_thread_loop
+ 4.04% 0.00% C2 CompilerThre libjvm.so [.] CompileBroker::invoke_compiler_on_method
+ 4.03% 0.00% C2 CompilerThre libjvm.so [.] C2Compiler::compile_method
+ 4.03% 0.00% C2 CompilerThre libjvm.so [.] Compile::Compile
+ 3.53% 0.68% Sweeper thread libjvm.so [.] CompiledMethod::cleanup_inline_caches_impl
因此,Java 11.0.13 和 -XX:ReservedCodeCacheSize=1024m 的 CPU 使用率与 Java 11.0.11 和 -XX:ReservedCodeCacheSize=375m 相同或略好
在我们的调查过程中,没有打印出有用的日志行,例如,我们希望看到类似“CodeCache 已满。编译器已被禁用”之类的内容。
jcmd <PID> VM.log what=codecache=info,sweep*=trace decorators=time,uptime,level,tags output=codecache-sweep.log
图片中的所有图表均使用时区 UTC+1。日志文件中的所有时间戳都是 UTC+0。
颜色只是为了显示随时间的变化。
codecache-sweep-375m.log.0
有趣的开始时间的正则表达式:\[2021-11-22T10:2[5-9].+'profiled nmethods'
- 每分钟扫描很多
codecache-sweep-512m.log.0
有趣的开始时间的正则表达式:\[2021-11-22T13:3[6-9].+'profiled nmethods
- 每分钟仍然有很多扫描。
codecache-sweep-1024m.log 一切正常,没什么特别的,平均每天 5 次扫描
请注意,在 Java 11.0.12(发行说明)中对代码缓存/代码堆组件进行了很多更改:搜索CodeHeap
您能否帮助我们理解 CodeHeap 'profiled nmethods' 缩小到非常低的水平并且不再增加的原因和含义?
更新 2021-12-28
使用 -XX:ReservedCodeCacheSize=375m 重新运行 11.0.11 和 11.0.13,jcmd <pid> Compiler.CodeHeap_Analytics aggregate
每 5 秒为 OpenJDK 团队收集更多数据。
带有日志和 CodeHeap_Analytics 数据的 zip
仍然可以重现更高级别的代码扫描程序调用(使用 11.0.13)。
Java 11.0.11 2h uptime, sweeper invocations:
grep -F '[codecache,sweep,start]' codecache-sweep-11.0.11_375m.log* | wc -l
14.458
Java 11.0.13 2h uptime, sweeper invocations:
grep -F '[codecache,sweep,start]' codecache-sweep-11.0.13_375m.log* | wc -l
41.901
有关详细统计信息,请从zip打开 jcmd_compiler_codeheap_analytics_aggregate 文件。