可靠地测量 JVM 分配
Reliably measure JVM allocations
我有两个相同算法的实现。我想验证它们中没有一个使用了比必要更多的内存,或者换句话说,它们分配了完全相同数量的对象。
我目前的解决方案是通过 threadMXBean.getThreadAllocatedBytes(threadId)
测量过程前后分配的字节数,并将其用作内存占用的近似值。
问题是这个方法不稳定,e.i。有时它 returns 比它应该的多得多。它特别显示在不分配对象的算法上。一个有问题的例子是一种对 int[]
.
求和的方法
实际代码 (Kotlin):
class MemAllocationTest {
private val threadMXBean = (ManagementFactory.getThreadMXBean() as? com.sun.management.ThreadMXBean)
?: throw RuntimeException("Runtime does not support com.sun.management.ThreadMXBean")
/**
* May run [block] several times
* */
private inline fun measureAllocatedBytes(block: () -> Unit): Long {
val threadId = Thread.currentThread().id
val before = threadMXBean.getThreadAllocatedBytes(threadId)
block()
val after = threadMXBean.getThreadAllocatedBytes(threadId)
return after - before
}
....
有更好的解决方案吗?
(我不知道如何用 JMH 做到这一点,但恕我直言,这是一个非常接近的话题)
我目前的解决方案是通过多次运行来收集统计信息:
private inline fun stabiliseMeasureAllocatedBytes(block: () -> Unit): Long {
val runs = List(7) { measureAllocatedBytes(block) }
val results = runs.drop(2) // skip warm-up
val counts = results.groupingBy { it }.eachCount()
val (commonResult, commonCount) = counts.entries.maxBy { (result, count) -> count }!!
if (commonCount >= results.size / 2)
return commonResult
else
throw RuntimeException("Allocation measurements vary too much: $runs")
}
JMH 有 -prof gc
分析器,它应该是准确的分配分析。尽管它在幕后使用相同的 ThreadMXBean
,但它可以过滤掉预热效果,并在多次 @Benchmark
调用中平均打嗝。那里的典型误差在 0.001 byte/op 以内。
我有两个相同算法的实现。我想验证它们中没有一个使用了比必要更多的内存,或者换句话说,它们分配了完全相同数量的对象。
我目前的解决方案是通过 threadMXBean.getThreadAllocatedBytes(threadId)
测量过程前后分配的字节数,并将其用作内存占用的近似值。
问题是这个方法不稳定,e.i。有时它 returns 比它应该的多得多。它特别显示在不分配对象的算法上。一个有问题的例子是一种对 int[]
.
实际代码 (Kotlin):
class MemAllocationTest {
private val threadMXBean = (ManagementFactory.getThreadMXBean() as? com.sun.management.ThreadMXBean)
?: throw RuntimeException("Runtime does not support com.sun.management.ThreadMXBean")
/**
* May run [block] several times
* */
private inline fun measureAllocatedBytes(block: () -> Unit): Long {
val threadId = Thread.currentThread().id
val before = threadMXBean.getThreadAllocatedBytes(threadId)
block()
val after = threadMXBean.getThreadAllocatedBytes(threadId)
return after - before
}
....
有更好的解决方案吗?
(我不知道如何用 JMH 做到这一点,但恕我直言,这是一个非常接近的话题)
我目前的解决方案是通过多次运行来收集统计信息:
private inline fun stabiliseMeasureAllocatedBytes(block: () -> Unit): Long {
val runs = List(7) { measureAllocatedBytes(block) }
val results = runs.drop(2) // skip warm-up
val counts = results.groupingBy { it }.eachCount()
val (commonResult, commonCount) = counts.entries.maxBy { (result, count) -> count }!!
if (commonCount >= results.size / 2)
return commonResult
else
throw RuntimeException("Allocation measurements vary too much: $runs")
}
JMH 有 -prof gc
分析器,它应该是准确的分配分析。尽管它在幕后使用相同的 ThreadMXBean
,但它可以过滤掉预热效果,并在多次 @Benchmark
调用中平均打嗝。那里的典型误差在 0.001 byte/op 以内。