1. JConsole内存监控
我们先来启动它,在命令行中输入jconsole,然后回车。如果是window电脑,需要装jdk,并且把环境变量配置完毕。
打开界面是这样的:
可以看到有一个本地进程,也就是JConsole本身的进程,我们试着连接它。
这就可以很清楚的看到了JConsole本身内存的信息,线程的信息,类的信息和CPU的信息。
一般我们查看内存泄露什么的还是得通过内存那一栏去查看,这里我感觉有点像AndroidStudio自带的内存检测工具一样。
2. JConsole线程监控
使用这个线程的监控,光通过看JConsole工具是看不出什么来的,所以我们需要一些例子:
1. 需要输入的,如果不往里面输入,这个程序就卡住了。
public class JpsClass {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
scanner.next();
}
}
看一下这个堆栈跟踪,卡在了一个readBytes的一个native方法,这就是需要等待我们输入的。
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的占用会比较高。
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的占有率也是比较低的。
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();
}
}
果然不出所料,卡住了,程序发生了死锁。
程序大致过程就是:第一个线程在执行的时候先拿到了obj1的锁,然后sleep的时候,第二个线程去拿obj2的锁,第一个线程sleep了100毫秒之后,也要去拿obj2,这时候obj2已经被第二个线程占用了,必须要等第二个线程执行完,释放了这把锁,才能让第一个线程继续执行;在第一个线程等待的时候,第二个线程需要拿obj1,但obj1也是被占用的,这样就造成第一个线程和第二个线程同时等待了。
来看看JConsole,已经将死锁的线程给列出来了。