Надежное измерение распределения JVM

У меня две реализации одного и того же алгоритма. Я хотел бы проверить, что не из них больше памяти, чем необходимо, или, другими словами, они выделяют точно такое же количество объектов.

Мое текущее решение состоит в том, чтобы измерить количество выделенных байтов до и после процедур через threadMXBean.getThreadAllocatedBytes(threadId) и использовать это как приближение объема памяти.

Проблема в том, что этот метод неустойчив, ei иногда возвращает гораздо большее число, чем нужно. Это особенно показывает алгоритмы, которые не выделяют объекты. Одним из проблемных примеров является метод, который суммирует int[] .

Фактический код (Котлин):

 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, но IMHO это очень близкая тема)

    JMH имеет профилировщик -prof gc , который должен быть точным с профилированием распределения. Хотя он использует один и тот же ThreadMXBean под обложкой, он может отфильтровывать эффекты разминки и усреднять икоты по нескольким @Benchmark . Типичные ошибки заключаются в пределах 0.001 байт / op.

    Мое текущее решение – собирать статистику с несколькими прогонами:

     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") }