Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
,当然也可以使用JMX接口来获取这些值。然而,确定使用内存的值并不可靠,可能的原因包括:
- 存在未收集的垃圾
- 如果GC没有进行压缩,就会存在碎片
Runtime.getRuntime().gc()
强制进行垃圾回收。我把这个实验性代码放在了这里:https://github.com/cruftex/java-memory-benchmark
如果我在读取这些值之前进行三次gc
调用,则会得到以下输出(对于jdk1.7.0_51,使用mvn test | grep loopCount
):testBaseline1: used=1084168, loopCount=0, total=124780544
testBaseline2: used=485632, loopCount=0, total=124780544
testBaseline3: used=483760, loopCount=0, total=124780544
testBaseline4: used=483800, loopCount=0, total=124780544
testBaseline: used=484160, loopCount=0, total=124780544
test100MBytes: used=105341496, loopCount=0, total=276828160
test127MBytes: used=133653088, loopCount=0, total=469901312
test27MBytes: used=28795528, loopCount=0, total=317755392
test10MBytes: used=10969776, loopCount=0, total=124784640
使用四个gc
调用(如所检查的),我得到了以下结果:
testBaseline1: used=483072, loopCount=0, total=124780544
testBaseline2: used=483728, loopCount=0, total=124780544
testBaseline3: used=483768, loopCount=0, total=124780544
testBaseline4: used=483808, loopCount=0, total=124780544
testBaseline: used=483848, loopCount=0, total=124780544
test100MBytes: used=105341504, loopCount=0, total=276828160
test127MBytes: used=133653096, loopCount=0, total=469901312
test27MBytes: used=28795536, loopCount=0, total=139239424
test10MBytes: used=10969784, loopCount=0, total=124784640
经验证,进行四次GC调用后,结果似乎是正确的。从GC统计输出中可以看到,第一次GC调用填充了老年代空间,第四次GC调用则减少了其大小。
2015-01-08T02:30:35.069+0100: [Full GC2015-01-08T02:30:35.069+0100: [Tenured: 0K->1058K(83968K)
2015-01-08T02:30:35.136+0100: [Full GC2015-01-08T02:30:35.136+0100: [Tenured: 1058K->1058K(83968K)
2015-01-08T02:30:35.198+0100: [Full GC2015-01-08T02:30:35.198+0100: [Tenured: 1058K->1058K(83968K)
2015-01-08T02:30:35.263+0100: [Full GC2015-01-08T02:30:35.264+0100: [Tenured: 1058K->471K(83968K)
最终代码获取内存使用值如下:
try {
Runtime.getRuntime().gc();
Thread.sleep(55);
Runtime.getRuntime().gc();
Thread.sleep(55);
Runtime.getRuntime().gc();
Thread.sleep(55);
Runtime.getRuntime().gc();
Thread.sleep(55);
} catch (Exception ignore) { }
long _usedMem;
long _total;
long _total2;
long _count = -1;
// loop to get a stable reading, since memory may be resized between the method calls
do {
_count++;
_total = Runtime.getRuntime().totalMemory();
try {
Thread.sleep(12);
} catch (Exception ignore) { }
long _free = Runtime.getRuntime().freeMemory();
_total2 = Runtime.getRuntime().totalMemory();
_usedMem = _total - _free;
} while (_total != _total2);
System.out.println(_testName + ": used=" + _usedMem + ", loopCount=" + _count + ", total=" + _total);
我不确定这种方法是否始终产生可靠的结果。所以有一些问题:
- 是否有一些最佳实践可以从Java程序获取可靠且可比较的基准值?
- 有什么想法可以调整(或实际上是取消)GC以满足使用情况?
- 是否有可靠的来源和可靠的行为来解释所需的四个GC调用?(顺便说一句:Java 8的表现方式相同)
- 是否有一种方式可以告诉JVM:“尽可能做最好的垃圾回收,我会等待”?
- 总的来说,可能是问题陈述最“未来证明”的可靠解决方案是什么?
更新:
虽然上面的一些问题与GC有关,但实际问题并非如此。我想找出应用程序在某个时间点的内存使用情况。一个可能的解决方案也是对所有对象进行深度搜索并汇总大小。
更新2:
与此问题相关的,我已经写了一篇详细的博客文章,讨论了不同的方法如何测量实际内存使用情况:
https://cruftex.net/2017/03/28/The-6-Memory-Metrics-You-Should-Track-in-Your-Java-Benchmarks.html