【问题标题】:JVM Compile Time vs Code CacheJVM 编译时间与代码缓存
【发布时间】:2018-07-13 19:19:22
【问题描述】:

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

我还观察到代码缓存也在缓慢上升。所以我决定将 Code Cache 预留提高到 500MB 进行测试。坏动作!现在它花费更多时间执行 JIT。

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

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

【问题讨论】:

    标签: java jvm jit jvm-codecache


    【解决方案1】:

    在 HotSpot JVM 中,所有 JIT 编译的方法都保留在 CodeCache 中,直到它们被回收。 UseCodeCacheFlushing 影响冷(但仍然有效)编译方法的回收。但是,CodeCache 也可能包含过时或无效的方法(“僵尸”),即使使用-XX:-UseCodeCacheFlushing,它们也会在下一个扫描周期中被清除。

    • tiered compilation 模式下(自JDK 8 起默认设置),可以使用不同级别的优化多次编译方法。一旦安装了方法的优化(第 4 层)版本,之前的版本就会过时,并且可以在该版本的所有激活完成后回收。
    • 当推测失败时(例如,在加载新类之后),推测编译的方法可能会变得无效。这种方法也会变成僵尸,以后可以回收。
    • 另一个例子是OSR compilation。这是一个专门为在方法运行时将执行从解释器转移到编译代码而编译的方法的一个版本。回答您的第三个问题,这是一种“临时”方法,在安装完整版本的已编译方法并完成所有 OSR 激活后就会过时。

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

    【讨论】:

    • 感谢您的详细解释。即使整个方法太大而无法编译,您是否知道 OSR 编译是否会运行?那么只有部分方法通过 OSR 编译,然后在完成时刷新?
    • @StanislavPalatnik 对于 OSR 和非 OSR 编译的限制基本相同。但上限足够大(8000 字节码),在实际应用中不会成为问题。
    猜你喜欢
    • 2010-12-31
    • 2014-11-19
    • 1970-01-01
    • 1970-01-01
    • 2021-02-19
    • 2015-08-20
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    相关资源
    最近更新 更多