提问人:Jesper 提问时间:11/17/2023 更新时间:11/17/2023 访问量:29
为什么即使代码缓存已满,代码缓存也不会刷新?
Why is the code cache not flushed even if code cache gets full?
问:
当我们从 JDK 17 切换到 JDK 21 时,我们经历了代码缓存行为方式的变化。
我们有运行串行 GC 收集器(由 JVM 选择)的小型服务,通常堆大约 400 mb,非分段代码缓存大约 40 mb,我想这在今天的标准中很低。下面是我们通常使用的已配置的 jvm 选项。
java
-Xmx400m
-Xss256k
-XX:MaxMetaspaceSize=120m
-XX:MaxDirectMemorySize=64m
-XX:CompressedClassSpaceSize=10m
-Xlog:gc*=info,codecache*=info // for debugging
-XX:ReservedCodeCacheSize=40m
-XX:MinHeapFreeRatio=50
-XX:MaxHeapFreeRatio=50
-XX:+HeapDumpOnOutOfMemoryError
-XX:-ShrinkHeapInSteps
-XX:MaxGCPauseMillis=10
在 JDK 17 中,代码缓存通常经常被垃圾回收。现在,如果一个服务不需要执行一个完整的GC(我看到tenured/old的堆占用率非常低),那么代码缓存永远不会刷新。代码缓存刷新接缝是整个 GC 周期的一部分。
真正奇怪的是,即使代码缓存被填满,它也不会执行任何主动扫描,即使我将 StartAggressiveSweepingAt 设置为一个较高的数字。我试图添加以下内容以在分配时触发代码缓存 GC,但没有任何运气。
-XX:SweeperThresholdStartAggressiveSweepingAt=20
-XX:SweeperThreshold=0.5
-XX:NmethodSweepActivity=500
问题是,当服务启动时,我确实会进行一些代码缓存扫描,以便启用“功能”。它将执行一些扫描,但这是包含短语 codecache 的 gc 日志的最后一次出现。观察日志中的“由于分配了 2.135% 以来的触发阈值 (2.125%) GC”。
[1891.824s][info][gc ] GC(125) Pause Young (Allocation Failure) 125M->69M(202M) 3.616ms
[1891.824s][info][gc,cpu ] GC(125) User=0.00s Sys=0.00s Real=0.00s
[1891.824s][info][gc,heap ] GC(125) Tenured: 71312K(142628K)->71312K(142628K)
[1938.484s][info][codecache ] Triggering threshold (2.125%) GC due to allocating 2.135% since last unloading (44.729% used -> 46.864% used)
[1938.485s][info][gc,start ] GC(126) Pause Young (GCLocker Initiated GC)
[1938.489s][info][gc,cpu ] GC(126) User=0.00s Sys=0.00s Real=0.01s
[1938.488s][info][gc,heap ] GC(126) DefNew: 44957K(64320K)->923K(64320K) Eden: 44605K(57216K)->0K(57216K) From: 351K(7104K)->923K(7104K)
[1938.489s][info][gc ] GC(126) Pause Young (GCLocker Initiated GC) 113M->70M(202M) 3.711ms
[1938.489s][info][gc,heap ] GC(126) Tenured: 71312K(142628K)->71312K(142628K)
[1938.489s][info][gc,metaspace ] GC(126) Metaspace: 92917K(93504K)->92917K(93504K) NonClass: 81847K(82176K)->81847K(82176K) Class: 11069K(11328K)->11069K(11328K)
[1994.918s][info][gc,start ] GC(127) Pause Young (Allocation Failure)
对我来说,听起来它应该在这一点上触发 GC(完整),但它没有发生。数小时没有完整的 GC。我查看了 JDK 的源代码,并希望安排完整的 GC。https://github.com/openjdk/jdk/blob/369bbecc0dab389b523c09bc332fe1cf6394cb26/src/hotspot/share/code/codeCache.cpp#L799-L804
我还发现这个 https://bugs.openjdk.org/browse/JDK-8290025,我确实认为这是改变行为的原因。
所以我的问题是,为什么在代码缓存填满时没有触发完整的 GC?我应该尝试任何参数吗?它仅在完整的 GC 上清除,由其他原因调用。
答: 暂无答案
评论