1. JConsole内存监控

我们先来启动它,在命令行中输入jconsole,然后回车。如果是window电脑,需要装jdk,并且把环境变量配置完毕。

虚拟机查看Java进程号 java虚拟机监控_ide

打开界面是这样的:

虚拟机查看Java进程号 java虚拟机监控_加锁_02

可以看到有一个本地进程,也就是JConsole本身的进程,我们试着连接它。

虚拟机查看Java进程号 java虚拟机监控_死锁_03

这就可以很清楚的看到了JConsole本身内存的信息,线程的信息,类的信息和CPU的信息。

虚拟机查看Java进程号 java虚拟机监控_死锁_04

一般我们查看内存泄露什么的还是得通过内存那一栏去查看,这里我感觉有点像AndroidStudio自带的内存检测工具一样。

 

2. JConsole线程监控

使用这个线程的监控,光通过看JConsole工具是看不出什么来的,所以我们需要一些例子:

1. 需要输入的,如果不往里面输入,这个程序就卡住了。

public class JpsClass {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        scanner.next();
    }

}

看一下这个堆栈跟踪,卡在了一个readBytes的一个native方法,这就是需要等待我们输入的。

虚拟机查看Java进程号 java虚拟机监控_ide_05

2. 是有一个空的while(true)会不停的请求执行。

public class JpsClass {

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
              while(true) {
              }
            }
        },"whileTrue").start();
    }

}

现在这个程序还在Runnable的状态,它一直在java文件的14行执行,对cpu的占用会比较高。

虚拟机查看Java进程号 java虚拟机监控_ide_06

3. 用一个Object锁住的状态。

public class JpsClass {

    static Object obj = new Object();
    public static void main(String[] args) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj) {
                    try {
                      obj.wait();
                    } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                }
            }
        },"wait").start();

    }

}

这个程序卡在了wait方法中,状态也是等待状态,它在等待一个notify把它唤醒,它是不抢占cpu资源的,所以它对cpu的占有率也是比较低的。

虚拟机查看Java进程号 java虚拟机监控_ide_07

 

3. JConsole线程死锁监控

什么是死锁?

假如说有两个临界资源AB,线程A正在访问资源A,为了保证线程安全,都会有一个锁,同时,线程B也在访问资源B,这时候是完全没有问题的。这时候,线程B要去访问资源A,因为资源A正在被线程A占用,锁还没有被释放,所以线程B只能等待;过了一会线程A也要去访问资源B,资源B同样也被线程B占用,也只能等待。这样就形成了循环等待,从而造成了死锁。

 

用一个先拿obj1加锁,然后obj2加锁,因为死锁是因为两个线程分别拿到了A和B的锁,其中一个线程想要去访问另一个资源,但另一个资源的锁还没有被释放的时候发生的,而cpu执行这个程序的速度是非常快的,这种情况的发生概率虽然存在,但是是很低的,所以需要在加锁的后面sleep一会,让它来等待。

public class DeadLock implements Runnable{

    private Object obj1;
    private Object obj2;
    DeadLock(Object obj1,Object obj2){
       this.obj1 = obj1;
       this.obj2 = obj2;
    }

    @Override
    public void run() {
       synchronized (obj1) {  //T1 -> obj1 ; T2 -> obj2
         try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
         synchronized (obj2) {
            System.out.println("lock");
        }
      }
    }

}

用一个测试类,起两个线程,分别用来访问A和B,B和A。

public class Test {

    public static void main(String[] args) {
        Object obj1 = new Object();
        Object obj2 = new Object();

        new Thread(new DeadLock(obj1, obj2)).start();
        new Thread(new DeadLock(obj2, obj1)).start();
    }

}

果然不出所料,卡住了,程序发生了死锁。

虚拟机查看Java进程号 java虚拟机监控_死锁_08

程序大致过程就是:第一个线程在执行的时候先拿到了obj1的锁,然后sleep的时候,第二个线程去拿obj2的锁,第一个线程sleep了100毫秒之后,也要去拿obj2,这时候obj2已经被第二个线程占用了,必须要等第二个线程执行完,释放了这把锁,才能让第一个线程继续执行;在第一个线程等待的时候,第二个线程需要拿obj1,但obj1也是被占用的,这样就造成第一个线程和第二个线程同时等待了。

 

来看看JConsole,已经将死锁的线程给列出来了。

虚拟机查看Java进程号 java虚拟机监控_虚拟机查看Java进程号_09