由于最近想自己动手测试一下String和StringBuffer的效率问题,需要获取程序运行时的内存占中信息,于是上网查了一下,根据查到的资料写了个程序,发现结果有问题,才发现查到的资料是错误的.所以在这里跟大家分享一下获取内存占用的正确方法
//程序开始时:(先调用一下垃圾回收,但是不一定立即执行)
Runtime.getRuntime().gc();
long initm=Runtime.getRuntime().freeMemory();
//程序结束时:
Runtime.getRuntime().gc();
long endm=Runtime.getRuntime().freeMemory();
//计算空闲差:
System.out.println(initm-endm);
gc
方法首先,在最开始调用Runtime.getRuntime().gc()
确实正确,但是注释中说
先调用一下垃圾回收,但是不一定立即执行
这句话就有问题了,java api文档中对gc方法的描述是这样的:
调用
gc
方法暗示着 Java 虚拟机做了一些努力来回收未用对象,以便能够快速地重用这些对象当前占用的内存。当控制权从方法调用中返回时,虚拟机已经尽最大努力从所有丢弃的对象中回收了空间。
也就是说,调用gc
时java虚拟机一定会进行垃圾回收.那么,为什么网上甚至许多教科书上说gc
方法并不一定会立即执行呢?个人猜测可能是运行完gc
方法后,内存的消耗量可能并没有显著的减少,因为这个时候可能并没有多少可以回收的垃圾.而过了一段时间后jvm又回收了一次内存,这次内存的消耗量明显的减少了.于是好多地方都会说gc
方法并不一定会立即执行.
而在第二次获取”内存”时,是不能调用gc
方法的,因为这样做会把一些中间的临时变量回收掉,导致获取到的”内存”并不一定是运行过程中真正使用的”内存”量.
freeMemory
方法那么使用freeMemory
方法获取前后两次的空闲内存,然后相减得到的不就应该是执行时消耗的内存吗?
如果内存总量不变,那么这种方法确实可以:
内存总量1 = 开始时空闲内存 + 开始时使用内存
内存总量2 = 结束时空闲内存 + 结束时使用内存
内存总量1 = 内存总量2
可以推出下面的式子:
开始时空闲内存 - 结束时空闲内存
= 内存总量1 - 开始时使用内存 - (内存总量2 - 结束时使用内存)
= 结束时使用内存 - 开始时使用内存
但是java api文档中关于totalMemory
方法的描述是:
返回 Java 虚拟机中的内存总量。此方法返回的值可能随时间的推移而变化,这取决于主机环境。
这句话是说内存总量是可能变化的, 也就是说,不能使用开始时空闲内存-结束时空闲内存
得到结束时使用内存 - 开始时使用内存
,也就是内存使用量.
那么,我们该如果获取运行时内存使用量呢?请继续往下看.
我们先说说在java程序中获取运行时内存,上代码:
Runtime run = Runtime.getRuntime();
System.in.read(); // 暂停程序执行
// System.out.println("memory> total:" + run.totalMemory() + " free:" + run.freeMemory() + " used:" + (run.totalMemory()-run.freeMemory()) );
run.gc();
System.out.println("time: " + (new Date()));
// 获取开始时内存使用量
long startMem = run.totalMemory()-run.freeMemory();
System.out.println("memory> total:" + run.totalMemory() + " free:" + run.freeMemory() + " used:" + startMem );
String str = "";
for(int i=0; i<50000; ++i){
str += i;
}
System.out.println("time: " + (new Date()));
long endMem = run.totalMemory()-run.freeMemory();
System.out.println("memory> total:" + run.totalMemory() + " free:" + run.freeMemory() + " used:" + endMem );
System.out.println("memory difference:" + (endMem-startMem));
/*
run.gc();
System.out.println("memory> total:" + run.totalMemory() + " free:" + run.freeMemory() + " used:" + (run.totalMemory()-run.freeMemory()) );
*/
代码中没有过于复杂的逻辑,关于调用System.in.read
方法暂停程序以及打印当前时间的原因我们到后面再解释.
这个程序在我的电脑上的一次输出为:
由于内存总量增长了5倍(内存总量就是在 任务管理器 中看到的内存占中量),导致空闲内存也增长了,那么开始时空闲内存 - 结束时空闲内存
就是负的…
这不排除是垃圾回收的结果,各位可以多执行几次看看结果:空闲内存确实是增长了.当然,各位也可以把代码的那几行注释取消了,看看执行过垃圾回收以后的内存变化.
在代码中获取程序内存占用量的优点:
缺点是
使用性能分析工具可以很方便的获取程序内存,cpu占用等信息.同时也不需要修改程序.java的性能分析工具还是不少的,我们这里使用的是jvisualvm.它最大的优点就是JDK自带,安装过JDK以后系统里就有jvisualvm了.我们可以在%JAVA_HOME%/bin
目录中找到jvisualvm.exe
,这个程序使用相当简单,我们就不说使用方法了,唯一需要注意的就是visualvm”打开”一个程序需要一段时间,如果在”打开”程序时程序结束了,那么我们什么信息也得不到,所以我们需要在程序开始出暂停程序.这就是我们在最开始处调用System.in.read
方法的原因.
打开visualvm,然后运行我们的程序,在VisualVM中双击打开我们的程序进行分析.在监视标签中可以查看内存使用量等信息.下面这幅图和上面的运行结果相对应:
|
|
OK,关于内存分析就说到这吧.如果有什么地方说错了,还请指教.
写于 2015/05/15
原文地址:http://blog.csdn.net/u011004037/article/details/45740673