2017-12-19 8 views
3

동일한 알고리즘을 두 번 구현했습니다. 나는 그들 중 누군가가 필요 이상으로 많은 메모리를 사용하고 있는지, 즉 정확히 같은 수의 객체를 할당했는지 확인하고 싶다.신뢰할 수있는 JVM 할당을 측정하십시오.

현재의 해결책은 절차 전후에 할당 된 바이트 수를 threadMXBean.getThreadAllocatedBytes(threadId)을 통해 측정하여 메모리 풋 프린트의 근사치로 사용하는 것입니다.

이 방법은 불안정합니다. 즉, e.i. 때로는 그것보다 훨씬 더 많은 수를 반환합니다. 특히 객체를 할당하지 않는 알고리즘을 보여줍니다. 문제가되는 한 가지 예는 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)

+2

질문과 직접적으로 관련이 없지만'Thread.currentThread(). id '가'block' 실행 후 어떻게 바뀔 수 있었는지 이해할 수 없습니까? – miensol

+0

@miensol 좋은 질문입니다. 편집 됨, 감사합니다 – voddan

답변

2

JMH가있다 -prof gc 프로파일 러. 할당 프로파일 링으로 정확해야합니다. 커버 아래에 동일한 ThreadMXBean을 사용하지만 워밍업 효과를 걸러 낼 수 있으며 복수 @Benchmark 호출에 걸친 딸꾹질을 평균화 할 수 있습니다. 일반적인 오류는 0.001 바이트/op 이내에 있습니다.

+0

단위 테스트에 임베드 될 수 있습니까? (바람직하게 bash 출력을 파싱하지 않고) – voddan

+0

음, 예, JMH API로 JMH 벤치 마크를 실행하고 프로파일 러 결과를 파싱 할 수 있습니다. –

+1

"나는 JMH가 그렇게하지 않는다는 것을 이해한다."라고 반응했습니다. :)하지만 아마도 단위 테스트를 위해서 원래 코드에 딸꾹질이있는 이유를 이해하는 것이 더 나을 것입니다. 필자는'block()'과'ThreadMXBean' 메소드의 처음 몇번의 호출을 걸러 내야 할 필요가 있다고 말하면서, JVM이 프로파일 링없이 lazy 초기화를 먼저 수행 할 수 있습니다. –

0

나의 현재 솔루션은 여러 실행하여 통계를 수집하는 것입니다이 :

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