快捷搜索:  汽车  科技

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:已退出的

jvm调优实战以及原理:jstack分析JVM问题(1)

实战操作:

用法一:查找高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和内存泄漏等问题,期待下回分解。

猜您喜欢: