我在 ByteWatcher (见我最后一篇 文章 )工作时,碰到了一些奇怪的事情。
这是一段用来查找在特殊线程上分配了多少内存的真实代码片段。
return (long) mBeanServer.invoke( name, GET_THREAD_ALLOCATED_BYTES, PARAMS, SIGNATURE );
全部上下文参见 这里 。
(ByteWatcher的工作方式是周期性地调用这个方法来监视内存分配。)
要注意的一个重点是,特别是当程序希望获得精确大小的内存分配时来调用上面的代码——会引起内存分配。
该调用引起的内存分配必须从返回数字中扣除,因此我们隔离了程序引起的内存分配,例如 调用meanBeanServer = 程序线程的内存分配 + 调用开销 。
我注意到 , 这个内存分配总是336字节。然而,当我在一个循环中调用这个方法时,发现了一些有趣的东西。几乎每隔一段时间,就会分配不同数量的内存。
测试如下:
<a href="http://www.jobbole.com/members/madao">@Test</a> 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
鉴于程序中绝对没有内存分配,这个谜题让我很困惑,为什么相同的调用有时会分配不同大小的内存。
总结下来,超过100万次运行,程序分配不同大小内存大约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优化了。
你可以在 这里 看到修正的代码,它包含几个阶段:
在构造器中有了这些代码,即使不带任何选项运行都不会有内存分配尖峰了。
原文链接: javacodegeeks 翻译:ImportNew.com -liken
译文链接:[]