意外分配– JIT编译抖动

在研究ByteWatcher时 (请参阅我的上一篇 文章 ),我遇到了一些非常奇怪的事情。

这是实际的代码段,用于找出特定线程上的分配量:

return (long) mBeanServer.invoke(
  name,
  GET_THREAD_ALLOCATED_BYTES,
  PARAMS,
  SIGNATURE
);
  • 有关完整上下文,请参见此处

(ByteWatcher的工作方式是定期调用此方法以监视分配。)

需要注意的一个重要方面,尤其是当想要为程序的分配提供准确的数字时,就是调用上面的代码本身–导致分配。

此调用引起的分配必须从返回的数中扣除,以便我们隔离程序导致的分配,即, 调用meanBeanServer =程序线程分配+调用开销

我注意到的是,此分配量通常为336个字节。 但是,当我在循环中调用此方法时,发现了一些有趣的东西。 每隔一段时间,它将分配不同的金额。

对于此测试:

@Test
  public void testQuietMeasuringThreadAllocatedBytes() {
    ByteWatcherSingleThread am = new ByteWatcherSingleThread();
    System.out.println("MeasuringCostInBytes = " + am.getMeasuringCostInBytes());
    long[] marks = new long[1_000_000];
    for (int i = 0; i < 1_000_000; i++) {
      marks[i] = am.threadAllocatedBytes();
    }

    long prevDiff = -1;
    for (int i = 1; i < 1_000_000; i++) {
      long diff = marks[i] - marks[i - 1];
      if (prevDiff != diff)
        System.out.println("Allocation changed at iteration " + i + "->" + diff);
      prevDiff = diff;
    }
  }

这是典型的结果:

MeasuringCostInBytes = 336
Allocation changed at iteration 1->336
Allocation changed at iteration 12->28184
Allocation changed at iteration 13->360
Allocation changed at iteration 14->336
Allocation changed at iteration 1686->600
Allocation changed at iteration 1687->336
Allocation changed at iteration 2765->672
Allocation changed at iteration 2766->336
Allocation changed at iteration 5458->496
Allocation changed at iteration 5459->336
Allocation changed at iteration 6213->656
Allocation changed at iteration 6214->336
Allocation changed at iteration 6535->432
Allocation changed at iteration 6536->336
Allocation changed at iteration 6557->8536
Allocation changed at iteration 6558->336
Allocation changed at iteration 7628->576
Allocation changed at iteration 7629->336
Allocation changed at iteration 8656->4432
Allocation changed at iteration 8657->336
Allocation changed at iteration 9698->968
Allocation changed at iteration 9699->336
Allocation changed at iteration 11881->1592
Allocation changed at iteration 11882->336
Allocation changed at iteration 12796->1552
Allocation changed at iteration 12797->336
Allocation changed at iteration 13382->456
Allocation changed at iteration 13383->336
Allocation changed at iteration 14844->608
Allocation changed at iteration 14845->336
Allocation changed at iteration 36685->304
Allocation changed at iteration 52522->336
Allocation changed at iteration 101440->400
Allocation changed at iteration 101441->336

鉴于程序中肯定没有分配,所以为什么同一调用有时分配不同的金额对我来说是一个谜。

总的来说,超过1,000,000次运行,该程序分配了大约25次不同的数量。 值得注意的是,经过10万次迭代后没有峰值。

我与亨氏·卡布兹(Heinz Kabutz)和克里斯·纽兰(Chris Newland)分享了这个问题。 Chris注意到分配是由JIT编译抖动引起的。 通过使用标志-Xint重新运行测试可以很清楚地看出这一点(仅以解释模式运行,即没有JIT编译)。 现在只有2个峰值。

MeasuringCostInBytes = 336
Allocation changed at iteration 1->336
Allocation changed at iteration 12->28184
Allocation changed at iteration 13->360
Allocation changed at iteration 14->336

同样使用-Xcomp标志运行(仅编译):

MeasuringCostInBytes = 336
Allocation changed at iteration 1->336
Allocation changed at iteration 12->29696
Allocation changed at iteration 13->360
Allocation changed at iteration 14->336

因此,现在我们可以非常有信心,正是导致准入分配的JIT编译抖动。

我不完全理解为什么会这样,但是我想这是可以理解的。 为了弥补这一点,我在ByteWatcher的构造函数中引入了一个校准阶段,Heinz进一步完善了该校准阶段。

您可以在此处看到校准代码但是它包含两个阶段:

  1. 调用该方法以计算线程在紧密循环中分配了多少(我们称其为100,000次)–允许JIT适当地预热代码,以便将其全部编译
  2. 等待50毫秒–这使JVM有机会完成编译抖动

在构造函数中使用此代码,即使没有特殊标志运行也不会出现分配高峰。

结论

  • JIT编译抖动导致一些分配
  • 在没有编译抖动(即仅解释或仅编译)的情况下运行程序会大大减少分配,但并不能完全消除分配。
  • 在100k运行之后,分配停止,这表明需要100k运行才能停止抖动。 这很有趣,因为我们知道代码应该在10k次迭代后进行编译。

翻译自: https://www.javacodegeeks.com/2015/09/an-unexpected-allocation-jit-compilation-jitter.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值