我有一个函数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)
使用的内存量应返回1MB。
memory{new Array[Byte](1024*1024)}{r=>println(s"$r byte")}
但是第一次调用内存会返回一个否定结果,后续调用测量(即使使用不同的bodys)内存使用情况就好了:
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 = sun.management.MemoryImpl@2f98635e
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
)。
为什么第一次测量会返回错误的结果?有没有办法解决这个问题,除了运行该方法两次?
在Arch Linux上使用Scala 2.12.1-20161205-201300-2787b47 (OpenJDK 64-Bit Server VM, Java 1.8.0_112)
。
谢谢!
答案 0 :(得分:4)
如果要检查JVM上的数据结构消耗了多少内存,则应查看JAMM等检测库。它的工作原理是遍历要测量的对象的对象图,并利用有关正在运行的JVM上的内存布局的知识。
请注意,您将获得的数据特定于您正在使用的 JVM版本和架构。在不同的体系结构上,由于指针大小和编码不同,内存消耗可能会有所不同。在不同的JVM上,甚至内存布局也可能不同。
尽管如此,这是在JVM上实现高效数据结构的强大工具。
以下是如何使用scala中的JAMM:
val o = new Array[Byte](1024*1024)
val mm = new MemoryMeter()
println("Size of new Array[Byte](1024*1024): " + mm.measureDeep(o))
结果如下:
Size of new Array[Byte](1024*1024): 1048592
JAMM库是一个挂钩到JVM的java代理。因此,使用JAMM需要下载jamm jar并向java选项添加参数(例如-javaagent:jamm-0.3.0.jar
),最好使用javaOptions sbt键。
请注意,如果依赖在您编写的某些数据结构的紧凑内存表示中,则应该使用自动化测试来确保内存中的表示形式为你期待。有关如何进行设置的灵感,这里有一个minimal project,可以为测试导入和配置JAMM java代理。
要玩游戏,您只需将测试代码添加到JammTest并使用sbt test:run
运行。
答案 1 :(得分:1)
您遇到的问题是未准确考虑内存使用情况以提高性能。这显示在两个方面
-XX:-UseTLAB
,即使new Object()
(假设GC没有发生),您也可以获得准确的帐户。