У меня есть функция memory
, которая принимает функцию и измеряет использование памяти его:Меры блока кода
import java.lang.management.ManagementFactory
def memory[T](
f: ⇒ T
)(
mu: Long ⇒ Unit
): T = {
val memoryMXBean = ManagementFactory.getMemoryMXBean
memoryMXBean.gc()
val usedBefore = memoryMXBean.getHeapMemoryUsage.getUsed
println(s"${memoryMXBean.getObjectPendingFinalizationCount()} pending, used $usedBefore")
val r = f
memoryMXBean.gc()
val usedAfter = memoryMXBean.getHeapMemoryUsage.getUsed
println(s"${memoryMXBean.getObjectPendingFinalizationCount()} pending, used $usedAfter")
mu(usedAfter - usedBefore)
r
}
Получение объема памяти, используемой new Array[Byte](1024*1024)
должен возвращать 1 МБ.
memory{new Array[Byte](1024*1024)}{r=>println(s"$r byte")}
Но самый первый вызов памяти возвращает отрицательный результат, последующие вызовы мера (даже с разными комбинезоны) использование памяти просто отлично:
scala> memory{new Array[Byte](1024*1024)}{r=>println(s"$r byte")}
0 pending, used 45145040
0 pending, used 45210384
65344 byte <- 65kb != 1MB
scala> memory{new Array[Byte](1024*1024)}{r=>println(s"$r byte")}
0 pending, used 45304512
0 pending, used 46353104
1048592 byte <- Correct
Где-то между этими двумя memoryMXBean.getHeapMemoryUsage
что-то будет освобожден, но там, где не ожидается освобождение ожидающего объекта. Такое поведение может быть также определено, когда у вас есть пустое тело (не забудьте перезапустить консоль SCALA, чтобы получить этот результат):
scala> memory{}{r=>println(s"$r byte")}
0 pending, used 44917584
0 pending, used 44025552
-892032 byte <- 800kb less memory?
scala> memory{}{r=>println(s"$r byte")}
0 pending, used 44070440
0 pending, used 44069960
-480 byte <- This is ok
Кроме того, выполняющему gc()
и getHeapMemoryUsage
на консоли производит этот результат:
scala> import java.lang.management.ManagementFactory; val memoryMXBean = ManagementFactory.getMemoryMXBean; memoryMXBean.setVerbose(true)
import java.lang.management.ManagementFactory
memoryMXBean: java.lang.management.MemoryMXBean = [email protected]
scala> memoryMXBean.gc(); memoryMXBean.getHeapMemoryUsage
[GC (System.gc()) 57400K->44462K(109056K), 0,0148555 secs]
[Full GC (System.gc()) 44462K->39602K(109056K), 0,2641397 secs]
res1: java.lang.management.MemoryUsage = init = 33554432(32768K) used = 41358440(40389K) committed = 111673344(109056K) max = 239075328(233472K)
scala> memoryMXBean.gc(); memoryMXBean.getHeapMemoryUsage
[GC (System.gc()) 46702K->40258K(111104K), 0,0025801 secs]
[Full GC (System.gc()) 40258K->39631K(111104K), 0,1988796 secs]
res2: java.lang.management.MemoryUsage = init = 33554432(32768K) used = 40583120(39631K) committed = 113770496(111104K) max = 239075328(233472K)
41358440 - 40583120 = 775320
, почти 800kb нет использование памяти (смотрите used
).
Почему первое измерение возвращает неправильный результат? Есть ли способ исправить это, кроме запуска метода дважды?
Использование Scala 2.12.1-20161205-201300-2787b47 (OpenJDK 64-Bit Server VM, Java 1.8.0_112)
в Arch Linux.
Спасибо!
Спасибо за ваше предложение. Это полезно для измерения размера возвращаемого значения, но вы не можете измерить общее распределение памяти побочных эффектов, не так ли? Например, у вас есть 'var L: List.empty [String]', и вы хотите измерить 'measure {fillLwithData()} {...}'. – amuttsch
Тогда я не понимаю, чего вы хотите. Измерьте потребление данных в структуре данных: используйте JAMM. Измерить общие ассигнования, включая временные: используйте что-то вроде https://github.com/google/allocation-instrumenter. Все, что происходит на JVM, можно точно измерить. –