JVM 编译时间与代码缓存

JVM Compile Time vs Code Cache

我一直在对我的应用程序进行基准测试并使用 JMC 对其进行分析。我注意到在负载下,它会执行相当多的 JIT 编译。如果我每秒发送大量事务,编译时间就会激增。编译时间总是随着对应用程序的任何重负载测试成比例增长。

我还观察到代码缓存也在缓慢上升。所以我决定将 Code Cache reserve 提高到 500MB 来测试。不好的举动!现在它花费更多时间执行 JIT。

然后我通过 -XX:-UseCodeCacheFlushing 明确禁用了代码缓存刷新。但是,我注意到峰值代码缓存使用量大于当前大小。这让我想到了几个问题:

  1. JVM 是否尝试缓存每个 JIT 编译?
  2. 即使我禁用了刷新,为什么峰值代码缓存大小仍大于当前大小?
  3. 是否有"temporary"函数结束后自动删除的编译代码?

在 HotSpot JVM 中,所有 JIT 编译的方法都保留在 CodeCache 中,直到它们被回收。 UseCodeCacheFlushing 影响冷(但仍然有效)编译方法的回收。但是,CodeCache 也可能包含过时或无效的方法 ("zombies"),即使使用 -XX:-UseCodeCacheFlushing.

,这些方法也会在下一个扫描周期中被清除
  • 模式下(自 JDK 8 以来的默认模式),一个方法可能会被编译多次,并使用不同级别的优化。一旦安装了方法的优化(第 4 层)版本,之前的版本就会过时,并且可以在该版本的所有激活完成后回收。
  • 推测编译的方法可能会在推测失败时变得无效(例如,在加载新的 class 之后)。这样的方法也会变成僵尸,以后可以回收。
  • 另一个例子是OSR compilation。这是一个版本的方法,专门编译用于在方法 运行 时将执行从解释器转移到编译代码。回答你的第三个问题,这是一种 "temporary" 方法,在安装完整版本的编译方法并完成所有 OSR 激活后,它就会过时。

有一个单独的 JVM 标志 -XX:-MethodFlushing 可以防止完全清除 CodeCache,包括僵尸方法。