你了解JDK的命令行工具吗
这里我们先写一个demo,写一个死循环
然后启动改程序
发现程序在这里已经开始运行了
使用top查看,到底是什么导致故障,如下图:
清楚的看到有一个java进程cpu占用率100%
我们另起一个终端,开始排查这个问题。
首先,使用jps指令,看看有哪些虚拟机进程
可以看到,我们运行的Test的进程ID是3667
拿到了这个进程号之后,我们需要具体到线程,所以就需要使用
top -Hp 命令来查看到底是该进程的哪个线程出问题了
可以看到在该进程中,ID为3368的线程占用cpu高,所以就排查该线程3368
因为在后面的异常信息栈中,线程ID是以16进制来体现的,所以这块我们先转成16进制
使用c语言的语法,以16进制进行打印
可以看到,16进制的3668是e54
现在我们打印线程的异常信息栈
找到了id为e54的线程,分析状态:
该线程处于运行状态,在第3行发现了问题,所以应该是个死循环,这时我们打开代码进行分析
确实是第3行在一直运行。
先来写一个死锁demo
public class DeadLock {
public static void main(String[] args) {
A a = new A();
B b = new B();
Thread thread = new Thread(() -> {
Thread.currentThread().setName("a线程");
a.foo(b);
});
Thread thread1 = new Thread(() -> {
Thread.currentThread().setName("b线程");
b.bar(a);
});
thread.start();
thread1.start();
}
}
class A {
public synchronized void foo(B b) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + "进入了A的foo方法");
System.out.println(Thread.currentThread().getName()+"准备调用b的last方法");
b.last();
}
public synchronized void last() {
System.out.println("调用了A类的last方法");
}
}
class B {
public synchronized void bar(A a) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + "进入了B的bar方法");
System.out.println(Thread.currentThread().getName()+"准备调用a的last方法");
a.last();
}
public synchronized void last(){
System.out.println("调用了B类的last方法");
}
}
这个程序是一个死锁demo,先来简单解释一下:
a线程调用了A类的foo方法,由于foo方法是一个同步方法,所以线程a对对象a加锁(哪个对象调用同步方法,就默认对该对象加锁),在a线程启动的同时,b线程也紧跟着启动了,b线程调用B类的bar方法,b线程对b对象加锁,这时b线程对b对象加锁。
a线程开始准备调用b的last方法,确发现b对象已经被b线程加锁了,然后b线程准备调用a的last方法,发现a也被加锁了,然后都在等待对方释放锁,进入死锁状态。
然后我们编译并运行该程序
首先使用top命令来查看是什么原因
发现没有占用cpu资源,也没有占用其他内存资源,大概率估计是死锁。
直接打印异常信息栈,使用jstack命令
因为我们对线程起了名字,所以排查的时候一眼就看到了a线程,b线程,仔细分析我标出的信息,如下图:
这两个线程都处于阻塞状态,并且每个线程都持有了一把锁,并且都在等待对方持有的锁,这样就可以很明显的分析出来,这是一个死锁。
具体在哪一行呢?
一个在20行,一个在13行
打开代码进行排查
发现确实是在这两个方法进行调用的时候互相等待对方释放锁,所以出现了死锁。
当出现Java应用大量消耗内存的时候,可能出现多种原因,可以从以下几种思路排查原因:
具体分析过程如下:
执行“jmap -histo:live 10765 | more”命令,以表格的方式显示存活对象的信息(已按对象所占bytes大小进行降序排列):
占内存最多的对象类型是org.apache.logging.log4j.core.async.RingBufferLogEvent,
总共18874368byte (18M),例子中属于正常使用。
tips:如果发现某类对象占用内存很大(几个G的大小),很可能是有问题的。
基本都是因为:该类对象创建太多,且一直未释放。比如:使用完IO资源后,未调用close()接口关闭、释放资源;又比如:消费者消费速度慢(或停止消费了),而生产者不断往队列中投递任务,导致队列中任务累积过多,任务对象占用内存太多而产生OutOfMemoryError
执行“pstree -p 10765 | wc -l”,查看进程内的线程数
其中,10765为进程ID。
每个线程需要分配线程栈内存,创建线程太多,可能导致OutOfMemoryError。
执行“jmap -heap 10765”,查看堆(新生代、老年代)内存分配大小及使用情况
执行“jstat -gc 10765 1000”,查看各个区内存使用情况及GC情况
具体字段含义,通过“man jstat”寻求帮助。
主要查看:
EC:Eden区容量,EU:Eden区已使用量,OC:Old区容量,OU:Old区已使用量;
YGC:YongGC次数,YGCT:YongGC耗时,FGC:FullGC次数,FGCT:FullGC耗时;
代码检查,重新检查代码中需要关闭资源的地方。