如何使用jvisualvm 使用进行cpu或内存抽样

Java(14)
一、JVisualVM能做什么
& & & VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带(java启动时不需要特定参数,监控工具在bin/jvisualvm.exe),能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的)。
&&&&&在JDK_HOME/bin(默认是C:\Program Files\Java\jdk1.6.0_13\bin)目录下面,有一个jvisualvm.exe文件,双击打开,从UI上来看,这个软件是基于NetBeans开发的了。
& & &可以进行远程和本地监控。远程监控需要打开jmx,下面内容会提到。
& & &其默认页面为:
左侧分为本地和远程。双击本地中VisualVM线程,可以看到如下监控内容:
具体的介绍参看:
二、准备模拟内存泄漏demo
& & & 1、定义静态变量HashMap
& & & 2、分段循环创建对象,并加入HashMap
& & & 代码如下:
import java.util.HashM
import java.util.M
public class CyclicDependencies {
//声明缓存对象
private static final Map map = new HashMap();
public static void main(String args[]){
Thread.sleep(10000);//给打开visualvm时间
} catch (InterruptedException e) {
e.printStackTrace();
//循环添加对象到缓存
for(int i=0; i&1000000;i++){
TestMemory t = new TestMemory();
map.put(&key&+i,t);
System.out.println(&first&);
//为dump出堆提供时间
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
for(int i=0; i&1000000;i++){
TestMemory t = new TestMemory();
map.put(&key&+i,t);
System.out.println(&second&);
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
for(int i=0; i&3000000;i++){
TestMemory t = new TestMemory();
map.put(&key&+i,t);
System.out.println(&third&);
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
for(int i=0; i&4000000;i++){
TestMemory t = new TestMemory();
map.put(&key&+i,t);
System.out.println(&forth&);
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(&qqqq&);
& & &3、配置jvm参数如下:
-XX:-UseGCOverheadLimit
-XX:MaxPermSize=50m
& & &4、运行程序并打卡visualvm监控
三、使用jVisualvm分析内存泄漏
& & & 1、查看Visual GC标签,内容如下,这是输出first的截图
& & & & & &&
这是输出forth的截图:
通过2张图对比发现:
老生代一直在gc,当程序继续运行可以发现老生代gc还在继续:
增加到了7次,但是老生代的内存并没有减少。说明存在无法被回收的对象,可能是内存泄漏了。
如何分析是那个对象泄漏了呢?打开抽样器标签:点击后如下图:
按照程序输出进行堆dump,当输出second时,dump一次,当输出forth时dump一次。
进入最后dump出来的堆标签,点击类:
点击右上角:“与另一个堆存储对比”。如图选择第一次导出的dump内容比较:
比较结果如下:
可以看出在两次间隔时间内TestMemory对象实例一直在增加并且多了,说明该对象引用的方法可能存在内存泄漏。
如何查看对象引用关系呢?
右键选择类TestMemory,选择“在实例视图中显示”,如下所示:
左侧是创建的实例总数,右侧上部为该实例的结构,下面为引用说明,从图中可以看出在类CyclicDependencies里面被引用了,并且被HashMap引用。
如此可以确定泄漏的位置,进而根据实际情况进行分析解决。
四、jVisualvm远程监控tomcat
& & &1、修改远程tomcat的catalina.sh配置文件,在其中增加:
& & & & & JAVA_OPTS=&$JAVA_OPTS
-Djava.rmi.server.hostname=192.168.122.128
-Dcom.sun.management.jmxremote.port=18999
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false&
这次配置先不走权限校验。只是打开jmx端口。
& & 2、打开jvisualvm,右键远程,选择添加远程主机:
& & & & &&
& &3、输入主机的名称,直接写ip,如下:
& & & & &&
& & & & 右键新建的主机,选择添加JMX连接,输入在tomcat中配置的端口即可。
& &4、双击打开。完毕!
五、扩展知识
线程死锁侦测
jvm优化建议
& &&&本质上是减少GC的次数。
如果是频繁创建对象的应用,可以适当增加新生代大小。常量较多可以增加持久代大小。对于单例较多的对象可以增加老生代大小。比如spring应用中。
GC选择,在JDK5.0以后,JVM会根据当前进行判断。一般执行-Server命令便可以。gc包括三种策略:串行,并行,并发。
吞吐量大大应用,一般采用并行收集,开启多个线程,加快gc的是否。
响应速度高的应用,一般采用并发收集,比如应用服务器。
& & &年老代建议配置为并发收集器,由于并发收集器不会压缩和整理磁盘碎片,因此建议配置:
-XX:+UseConcMarkSweepGC
#并发收集年老代
-XX:CMSInitiatingOccupancyFraction=80 # 表示年老代空间到80%时就开始执行CMS
-XX:+UseCMSCompactAtFullCollection # 打开对年老代的压缩。可能会影响性能,但是可以消除内存碎片。
-XX:CMSFullGCsBeforeCompaction=10 # 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此参数设置运行次
FullGC以后对内存空间进行压缩、整理。&
直接运行linux上的jvisualvm
& & &下载X-Manager,可以将试图展现在本地机器上。
不受此jvm支持
& & &保证jvisualvm所属jdk版本和linux上一致。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:736066次
积分:12568
积分:12568
排名:第889名
原创:464篇
转载:177篇
(1)(13)(15)(13)(5)(7)(3)(8)(20)(22)(6)(8)(5)(5)(8)(31)(21)(16)(24)(26)(72)(38)(20)(7)(32)(18)(23)(91)(80)The requested URL '/content/14/844.shtml' was not found on this server.有人会因为无法作出决定就推迟决定,然而实际上推迟决定是最差的决定。在推迟决定期间,时间悄悄流逝,你却没有任何一条路上的积累,白白浪费了时间。来自《暗时间》
VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带(java启动时不需要特定参数,监控工具在bin/jvisualvm.exe)。
VisualVM,能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的).
从界面上看还是比较简洁的,左边是树形结构,自动显示当前本机所运行的Java程序,还可以添加远程的Java VM,其中括号里面的PID指的是进程ID。OverView界面显示VM启动参数以及该VM对应的一些属性。Monitor界面则是监控Java堆大小,Permgen大小,Classes和线程数量。jdk不同版本中界面会不太一致,如有的含cpu监控,有的则不含(jdk1.6.0_10未包含)。
官网上关于jvisualvm的用法介绍&
/javase/6/docs/technotes/tools/share/jvisualvm.html&&
作用:JVM和监控的应用程序运行在不同的服务器上,减轻应用程序的负担,特别是HeapDump的时候,应用常能够续负担很大。&
VisualVM使用简单,几乎0配置,功能还是比较丰富的,几乎囊括了其它JDK自带命令的所有功能。
Dump堆(本地进程)
Dump线程(本地进程)
打开堆Dump。堆Dump可以用jmap来生成。
打开线程Dump
生成应用快照(包含内存信息、线程信息等等)
性能分析。 :idea: CPU分析(各个方法调用时间,检查哪些方法耗时多),内存分析(各类对象占用的内存,检查哪些类占用内存多)
本地监控不需要配置,只要打开某个JAVA程序就会自动的加入到本地监控中。
远程监控: 本机的VisualVM就必须和远程的JVM要进行通信,&Visualvm目前支持两种remote connection方式,分别是jstatd和JMX方式。
远程监控某个中间件时,需要修改中间件的启动文件,添加上关于jmx等的信息。
1、远程监控tomcat,jmx方式。
服务器&上的&tomcat&配置&jvm&启动参数。在&tomcat&的&catalina.bat&中添&加如下参数:&
JAVA_OPTS="$JAVA_OPTS&-Djava.rmi.server.hostname=192.168.0.237 &&&
-Dcom.sun.management.jmxremote.port=18999&&&&&&&&&&&&&&&&&&&&&&-Dcom.sun.management.jmxremote.ssl=false&&&&&&&&&&&&&&&&&&&&&&&-Dcom.sun.management.jmxremote.authenticate=false"上述参数未设用户名与密码登录。
&&客户端VisualVM配置&(我客户端用的是WinXP).&&&&&a.直接反键点击Remote,选择Add Remote Host...&&&&&b.在弹出的界面中输入远程机器的IP地址(192.168.0.237),这个IP地址会加入到Remote节点下.&&&&&c.反键点击这个IP地址,选择Add JMX Connection, 在弹出的界面中输入刚配置的端口号(18999), 这个连接会加入到该IP节点下.&&&&&d.反键点击这个连接,选择Open.
此处参数设置与jconsole工具远程监控tomcat相同。
2、监视websphere服务器JVM的配置
&http://xjsunjie./1880
&在启动文件里配置。
3、jstatd 配置。
http://zhouanya./0017
四、使用实例
1、插件&Visual GC"使用。
转自:http://supercharles888./9790
安装 &Visual GC"插件:
这个插件是jvisualvm的插件,它非常强大,可以动态的对指定的进程进行监控,并且来通过统计面板来分类显示出各项任务/事件的总时间开销:
安装方法: Tool-&Plugin-&Available Plugins:
重启Visual VM 之后,就可以看到这个"Visual GC"已经被正确的显示了。
实战: 用Visual VM和Visual GC来优化我们的Eclipse启动:
首先,我们启动eclipse:
在ps -ef|grep eclipse(windows则是任务管理器)中,我们可以从看到这个进程id为32561
我们从Visual VM中找到对应的process id:
我们切换到 &Visual GC"标签页,它会显示启动eclipse的所有测量数据:
从上图中我们可以很明显的看出来,主要的时间开销在以下2方面:
(1)编译时间有点长,用了3.794秒,这个时间主要是用来校验eclipse平台本身的字节码了,所以我们需要关闭字节码校验,让启动时候不会去校验平台本身(也是java写的)的字节码,为了达到这个目的,我们只需要在eclipse启动参数中加上-Xverify:none
如下所示,因为我们用的是Spring Source Tool Suite,所以我们在STS.ini中增加这一行。
(2)另外一个大问题就是类加载时间,它有2部分组成,因为类有2部分组成,一是eclipse平台自带的类,二是它所使用的插件的类文件,我们可以在eclipse启动的时候关闭不必要的插件加载来减少类加载时间,方法是Preference-&General-&Startup and Shutdown
校验结果:
现在我们把eclipse关闭并且重新打开,这会启动一个新的进程,id为32696,我们把这次Visual GC的测量图和原来的进行比较:
从这里可以看出来,时间被明显的缩短了,编译时间从3.794秒缩短到2.155秒,提升百分比为43.1%。而类加载时间从18.424秒缩短到10.208秒,提升百分比为44.6%。
额外步骤:
对于一些其他的启动参数,比如初始内存,最大内存,Gem,Perm的参数。
2、将dump的文件,使用其进行查看。
1)dump文件。
2)将dump文件传至jvisualvm本机,点击&文件&-》&装入&,选择第一步生成的dump文件。
a.摘要标签
可查看dump的各项信息。
双击某个类,点击实例视图。
c.实例试图
五、远程监控内存泄露、解决内存溢出问题
1)内存泄露、溢出的异同
同:都会导致应用程序运行出现问题,性能下降或挂起。
v内存泄露是导致内存溢出的原因之一;内存泄露积累起来将导致内存溢出。
v内存泄露可以通过完善代码来避免;内存溢出可以通过调整配置来减少发生频率,但无法彻底避免。
2)监测内存泄漏
&内存泄漏是指程序中间动态分配了内存,但在程序结束时没有释放这部分内存,从而造成那部分内存不可用的情况,重启计算机可以解决,但也有可能再次发生内存泄露,内存泄露和硬件没有关系,它是由软件设计缺陷引起的。 &
&内存泄漏可以分为4类:
a.&常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
b.偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
c.一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
d.隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。
3)Heap dump&分析
每隔一段时间给所检测的java应用做一次heap dump:
(或者在响应应用pid上鼠标右键heap dump)弹出以下提示框:
在应用服务器将此文件下载到jvisual vm所在的机器上,file--load打开此文件,如下面三图所示:
对比上面三个截图,发现似乎有个东西在急速飙升,仔细一看是这个对象:org.eclipse.swt.graphics.Image 在第一章图中这个还没排的上,第二次dump已经上升到5181个,第三次就是7845个了。涨速相当快,而且和任务管理器里面看到的GDI数量增涨一致,就是它了。
问题到这儿就比较清楚了,回到代码里面仔细一看可以发现,是某个地方反复的用图片来创建Image对象导致的,改掉以后搞定问题。
到这里其实我想说的是,Java使用起来其实要比C++更容易导致内存泄漏。对于C++来说,每一个申请的对象都必须明确释放,任何没有释放的对象都会导致memleak,这是不可饶恕的,而且这类问题已经有很多工具和方法来解决。但是到了Java里面情况就不同了,对于Java程序员来说对象都是不需要也无法主动销毁的,所以一般的思路是:随用随new,用完即丢。很多对象具体的生命周期可能连写代码的人自己也不清楚,或者不需要清楚,只知道某个时刻垃圾收集器会去做的,不用管。但很可能某个对象在&用完即丢&的时候在另一个不容易发现的地方被保存了一个引用,那么这个对象就永远不会被回收。更加糟糕的是整个程序从设计之初就没有考虑过对象回收的问题,对于C++程序员来说memleak必然是一个设计错误,但是对Java程序员来说这只是一个疏忽,而且似乎没有什么好的办法来避免。今天看到的这个问题是因为GDI泄漏会造成严重后果才被重视,但如果仅仅是造成内存泄漏,那这个程序可能得连续跑上个十天半个月才会发现问题。最后就是,对于c++,错误的代码在测试阶段就可以快速的侦测出哪怕一个byte的memleak并加以改正,但是对于java程序,理论上没有哪个工具能够知道是不是有泄漏,因为除了作者自己以外没有人能够知道一个被引用的对象是不是应该被销毁,只有通过大量的,长期的压力测试才能发现问题,这是很危险的一件事情。
4)解决内存溢出问题
1、java.lang.OutOfMemoryError: PermGen space
JVM管理两种类型的内存,堆和非堆。堆是在JVM启动时创建;非堆是留给JVM自己用的,用来存放类的信息的。它和堆不同,运行期内GC不会释放空间。如果web app用了大量的第三方jar或者应用有太多的class文件而恰好MaxPermSize设置较小,超出了也会导致这块内存的占用过多造成溢出,或者tomcat热部署时侯不会清理前面加载的环境,只会将context更改为新部署的,非堆存的内容就会越来越多。
PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中,它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用中有很CLASS的话,就很可能出现PermGen space错误,这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。
如上图所示,PermGen在程序运行一段时间后超出了我们指定的128MB,通过Classes视图看到,Java在运行的同时加载了大量的类到内存中。PermGen会存储Jar或者Class的描述信息;所以在class大量增加的同时PermGen超出了我们指定的范围。为了让应用稳定,我们需要探寻新的PermGen范围。检测时段时候后(如下图)发现PermGen在145MB左右稳定,那么我们就得到了稳定的新参数;这样PermGen内存溢出的问题得到解决。
2、java.lang.OutOfMemoryError: Java heap space
第一种情况是个补充,主要存在问题就是出现在这个情况中。其默认空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。如果内存剩余不到40%,JVM就会增大堆到Xmx设置的值,内存剩余超过70%,JVM就会减小堆到Xms设置的值。所以服务器的Xmx和Xms设置一般应该设置相同避免每次GC后都要调整虚拟机堆的大小。假设物理内存无限大,那么JVM内存的最大值跟操作系统有关,一般32位机是1.5g到3g之间,而64位的就不会有限制了。
注意:如果Xms超过了Xmx值,或者堆最大值和非堆最大值的总和超过了物理内存或者操作系统的最大限制都会引起服务器启动不起来。
垃圾回收GC的角色,JVM调用GC的频度还是很高的,主要两种情况下进行垃圾回收:
一个是当应用程序线程空闲;另一个是java内存堆不足时,会不断调用GC,若连续回收都解决不了内存堆不足的问题时,就会报out of memory错误。因为这个异常根据系统运行环境决定,所以无法预期它何时出现。
根据GC的机制,程序的运行会引起系统运行环境的变化,增加GC的触发机会。
为了避免这些问题,程序的设计和编写就应避免垃圾对象的内存占用和GC的开销。显示调用System.GC()只能建议JVM需要在内存中对垃圾对象进行回收,但不是必须马上回收。一个是并不能解决内存资源耗空的局面,另外也会增加GC的消耗。
如何避免内存泄漏、溢出
1)尽早释放无用对象的引用。
好的办法是使用临时变量的时候,让引用变量在退出活动域后自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄露。
2) 程序进行字符串处理时,尽量避免使用String,而应使用StringBuffer。
因为每一个String对象都会独立占用内存一块区域,如:
1.String str = "aaa"; & &
2.String str2 = "bbb"; & &
3.String str3 = str + str2; & &
4.// 假如执行此次之后str , str2再不被调用,那么它们就会在内存中等待GC回收; & &
5.// 假如程序中存在过多的类似情况就会出现内存错误; &
3) 尽量少用静态变量。
因为静态变量是全局的,GC不会回收。
4) 避免集中创建对象尤其是大对象,如果可以的话尽量使用流操作。
JVM会突然需要大量内存,这时会触发GC优化系统内存环境; 一个案例如下:
1.// 使用jspsmartUpload作文件上传,运行过程中经常出现java.outofMemoryError的错误, & &
2.// 检查之后发现问题:组件里的代码 & &
3.m_totalBytes = m_request.getContentLength(); & &
4.m_binArray = new byte[m_totalBytes]; & &
5.// totalBytes这个变量得到的数极大,导致该数组分配了很多内存空间,而且该数组不能及时释放。 & &
6.// 解决办法只能换一种更合适的办法,至少是不会引发outofMemoryError的方式解决。 & &
7.// 参考:http://bbs./blog/more.asp?name=hongrui&id=3747 &
5) 尽量运用对象池技术以提高系统性能。
生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。
6) 不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。
可以适当的使用hashtable,vector 创建一组对象容器,然后从容器中去取那些对象,而不用每次new之后又丢弃。
7) 优化配置。
a.设置-Xms、-Xmx相等;
b.设置NewSize、MaxNewSize相等;
c.设置Heap size, PermGen space:
六、使用过程中遇到的一些问题与疑问
问题1:从服务器dump堆内存后文件比较大(3.5G左右),加载文件、查看实例对象都很慢,还提示配置xmx大小。在windows上如何配置xmx大小?
表明给VisualVM分配的堆内存不够,找到${visualvm}/etc/visualvm.conf (如:C:\Program Files\Java\jdk1.6.0_10\lib\visualvm\etc)这个文件,修改
default_options=&-J-Xms24m -J-Xmx192m&
default_options=&-J-Xms24m -J-Xmx1024m&(
再重启VisualVM就行了。
对于&堆 dump&来说,在远程监控jvm的时候,VisualVM是没有这个功能的,只有本地监控的时候才有。另外,就算是本地监控,它在dump和得到实例的 速度那是相当的慢的。所以鉴于这几个原因,不建议用VisualVM,而是用jmap加上Mat来分析内存情况。
参考资料:
1、/ICkengine/archives/106440.shtml
2、http://freewind.me/blog/.html
3、http://supercharles888./9790
4、http://zhouanya./0017
阅读(...) 评论() &

我要回帖

更多关于 jvisualvm 内存分析 的文章

 

随机推荐