jvm调优实战以及原理:jstack分析JVM问题
jvm调优实战以及原理:jstack分析JVM问题用户自定义的线程池JIT线程[CompilerThread3]Tomcat Boss线程(IO线程)DubboNetty
背景:jstack用于生成JVM当前时刻的线程快照,可以定位线程死锁、死循环、请求外部时长过长导致线程停顿等问题。
线程分类:
gc线程
tomcat worker线程
Tomcat Boss线程(IO线程)
Dubbo
Netty
JIT线程[CompilerThread3]
用户自定义的线程池
线程状态:
NEW:未启动的。不会出现在Dump中。
RUNNABLE:在虚拟机内执行的。运行中状态,可能里面还能看到locked字样,表明它获得了某把锁。
BLOCKED:受阻塞并等待监视器锁。被某个锁(synchronizers)給block住了。
WATING:无限期等待另一个线程执行特定操作。等待某个condition或monitor发生,一般停留在park() wait() sleep() join() 等语句里。
TIMED_WATING:有时限的等待另一个线程的特定操作。和WAITING的区别是wait() 等语句加上了时间限制 wait(timeout)。
TERMINATED:已退出的
实战操作:
用法一:查找高CPU占用的线程(死循环)1 . top -Hp 进程ID 查看进程内所有的线程状态;
2. printf "%x\n" 线程ID 把CPU占用高的线程ID转为16进制;
3. jstack -l 进程ID > log.txt;
在log.txt里面查找第二步获得的线程ID 查看调用堆栈
"http-nio2-9386-exec-25" #52 daemon prio=5 os_prio=31 tid=0x00007ff9c46d6800 nid=0x7c03 waiting on condition [0x000070000a651000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007816cdbc8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:103) at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:31) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748) 用法二: 查找死锁
我们首先故意制造一个死锁现场:
private Object lock1 = new Object(); private Object lock2 = new Object(); @RequestMapping("/deadlock") public String deadlock(){ Thread thread1 = new Thread(){ @Override public void run() { super.run(); synchronized (lock1){ LockSupport.parkNanos(1000000); synchronized (lock2){ } } } }; thread1.setName("yzy"); thread1.start(); Thread thread2 = new Thread(){ @Override public void run() { super.run(); synchronized (lock2){ LockSupport.parkNanos(1000000); synchronized (lock1){ } } } }; thread2.setName("yzk"); thread2.start(); return "OK"; }
使用同上面相同的方法得到日志文件如下:
Found one Java-level deadlock: ============================= "yzk": waiting to lock monitor 0x00007ff9c1803618 (object 0x0000000781c4e118 a java.lang.Object) which is held by "yzk" "yzk": waiting to lock monitor 0x00007ff9c18036c8 (object 0x0000000781c4e108 a java.lang.Object) which is held by "yzy" "yzy": waiting to lock monitor 0x00007ff9c1803618 (object 0x0000000781c4e118 a java.lang.Object) which is held by "yzk" Java stack information for the threads listed above: =================================================== "yzk": at com.kuaikan.data.horadric.backend.controller.PingController$2.run(PingController.java:48) - waiting to lock <0x0000000781c4e118> (a java.lang.Object) "yzk": at com.kuaikan.data.horadric.backend.controller.PingController$2.run(PingController.java:51) - waiting to lock <0x0000000781c4e108> (a java.lang.Object) - locked <0x0000000781c4e118> (a java.lang.Object) "yzy": at com.kuaikan.data.horadric.backend.controller.PingController$1.run(PingController.java:36) - waiting to lock <0x0000000781c4e118> (a java.lang.Object) - locked <0x0000000781c4e108> (a java.lang.Object) 最佳实践:
Thread 和 ThreadPool 都需要定义名字
读懂日志文件locked <0x0000000781c4e118> (a java.lang.Object)
通过synchronized关键字 成功获取到了对象的锁
waiting to lock <0x0000000781c4e108> (a java.lang.Object)
通过synchronized关键字 没有获取到对象的锁 线程在监视器的进入区等待。在调用栈顶出现 线程状态为Blocked
waiting on condition [0x000070000a651000]
通过synchronized关键字 成功获取到了对象的锁后 调用了wait方法 进入对象的等待区等待。在调用栈顶出现 线程状态为WAITING或TIMED_WATING
除此以外:
jstat -gc、jmap -dump:live format=b file=dump.hprof [pid] 等工具分析GC和内存泄漏等问题,期待下回分解。