VisualVM有四个区域
1.内存堆,堆就是放对象的,我们可以点堆Dump,通过分析ump结果,看有哪些对象是非常占内存的。
遇到内存问题,最好是希望能够直观的看到Java程序堆中现在有哪些对象,有哪些对象数目一直在递增而没有被回收。为此需要借助工具来排查了,visualVM是非常好的能满足需求的一个工具。
点击Classes, 发现char[]所占用的内存是最大的
双击它,得到如下Instances结果,Instances是按Size由大到小排列的,第一个就是最大的, 展开Field区域的 values
通过查看堆内存,我们可以得到哪些是大对象
我们再安装个Visual GC插件(visualVM-工具-插件-可用插件)就可以分析查看内存的GC情况
下面转自https://blog.csdn.net/kl28978113/article/details/53817827
准备模拟内存泄漏demo
1、定义静态变量HashMap
2、分段循环创建对象,并加入HashMap
import java.util.HashMap;
import java.util.Map;
public class CyclicDependencies {
//声明缓存对象
private static final Map map = new HashMap();
public static void main(String args[]){
try {
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出堆提供时间
try {
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");
try {
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");
try {
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");
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("qqqq");
}
}
配置JVM参数:-Xms512m -Xmx512m -XX:-UseGCOverheadLimit -XX:MaxPermSize=50m
1.查看Visual GC标签,内容如下,这是输出first的截图
2.这是输出forth的截图:
通过2张图对比发现:
老生代一直在gc,当程序继续运行可以发现老生代gc还在继续:
增加到了7次,但是老生代的内存并没有减少。说明存在无法被回收的对象,可能是内存泄漏了。
如何分析是那个对象泄漏了呢?打开抽样器标签:点击后如下图:
按照程序输出进行堆dump,当输出second时,dump一次,当输出forth时dump一次。
进入最后dump出来的堆标签,点击类:
比较结果如下:
可以看出在两次间隔时间内TestMemory对象实例一直在增加并且多了,说明该对象引用的方法可能存在内存泄漏。
如何查看对象引用关系呢?
右键选择类TestMemory,选择“在实例视图中显示”,如下所示:
左侧是创建的实例总数,右侧上部为该实例的结构,下面为引用说明,从图中可以看出在类CyclicDependencies里面被引用了,并且被HashMap引用。
如此可以确定泄漏的位置,进而根据实际情况进行分析解决。
因为在上面这个例子中,我们是一直往Map里加对象的,没有执行remove对象操作,也没有释放Map,所以通过visualVM可以看到堆占用的内存是很高的,当我执行remove对象操作或把map设为空时, 可以很直观的看到堆内存在GC时,一下子回收了很多对象
2.CPU
CPU 性能分析的主要目的是统计函数的调用情况及执行时间,或者更简单的情况就是统计应用程序的 CPU 使用情况。
过高的 CPU 使用率可能是由于我们的项目中存在低效的代码;
在我们对程序施压的时候,过低的 CPU 使用率也有可能是程序的问题。
使用抽样器分析CPU
可以看到是具体方法占用CPU的时间数据,方便我们定位到,效率低的方法
3.线程
当我们对一个多线程应用程序进行调试或者开发后期做性能调优的时候,往往需要了解当前程序中所有线程的运行状态,是否有死锁、热锁等情况的发生,从而分析系统可能存在的问题。
在 VisualVM 的监视标签内,我们可以查看当前应用程序中所有活动线程(Live threads)和守护线程(Daemon threads)的数量等实时信息。
来一段死锁的程序,看VisualVM 能否分析出来
package jvisualVM;
public class DeadLock {
public static void main(String[] args) {
Resource r1 = new Resource();
Resource r0 = new Resource();
Thread myTh1 = new LockThread1(r1, r0);
Thread myTh0 = new LockThread0(r1, r0);
myTh1.setName("DeadLock-1 ");
myTh0.setName("DeadLock-0 ");
myTh1.start();
myTh0.start();
}
}
class Resource {
private int i;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
class LockThread1 extends Thread {
private Resource r1, r2;
public LockThread1(Resource r1, Resource r2) {
this.r1 = r1;
this.r2 = r2;
}
@Override
public void run() {
int j = 0;
while (true) {
synchronized (r1) {
System.out.println("The first thread got r1's lock " + j);
synchronized (r2) {
System.out.println("The first thread got r2's lock " + j);
}
}
j++;
}
}
}
class LockThread0 extends Thread {
private Resource r1, r2;
public LockThread0(Resource r1, Resource r2) {
this.r1 = r1;
this.r2 = r2;
}
@Override
public void run() {
int j = 0;
while (true) {
synchronized (r2) {
System.out.println("The second thread got r2's lock " + j);
synchronized (r1) {
System.out.println("The second thread got r1's lock" + j);
}
}
j++;
}
}
}
打开VisualVM检测到的JVM进程,我们可以看到这个tab在闪,VisualVM已经检测到我这个package下面的DeadLock类出错了
切换到Thread tab, 可以看到死锁了, Deadlock detected!
另外可以点击Thread Dump 线程转储,进一步分析,在这里就不赘述了,有兴趣的读者可以自行实验。