Thread Dump提供了当前活动线程的快照,及JVM中所有Java线程的堆栈跟踪信息,堆栈信息一般包含完整的类名及所执行的方法,如果可能的话还有源代码的行数。
通过Thread Dump可以将问题直接定位到应用程序的代码行上。
能诊断的问题
二、获取Dump日志 1. jstack
- 查找内存,常见的是程序里load大量的数据到缓存。
- 可以发现死锁线程。
jstack -l> pid: Java 应用的进程 id ,也就是需要抓取 dump 文件的应用进程 id。 file-path: 保存 dump 文件的路径。
例子:用 jstack 生成 dump 文件到 /opt/tmp/threadDump.txt 目录下。
jstack -l 37320 > /opt/tmp/threadDump.txt
2. jmap
jmap(JVM Memory Map)一方面可以获取dump文件(堆转储快照文件,二进制文件),它还可以获取目标Java进程的内存相关信息,包括Java堆各区域的使用情况、堆中对象的统计信息、类加载信息等。
2.1. 进程堆的分配和使用情况: -heap
比如看Flink TaskManagerRunner进程堆的分配和使用情况
jps |grep TaskManagerRunner //171266 TaskManagerRunner jmap -heap 171266 > jmap1.log
Attaching to process ID 171266, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.152-b16 using thread-local object allocation. Garbage-First (G1) GC with 13 thread(s) Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 536870912 (512.0MB) NewSize = 1363144 (1.2999954223632812MB) MaxNewSize = 321912832 (307.0MB) OldSize = 5452592 (5.1999969482421875MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 268435456 (256.0MB) G1HeapRegionSize = 1048576 (1.0MB) Heap Usage: G1 Heap: regions = 512 capacity = 536870912 (512.0MB) used = 194198464 (185.20208740234375MB) free = 342672448 (326.79791259765625MB) 36.172282695770264% used G1 Young Generation: Eden Space: regions = 143 capacity = 337641472 (322.0MB) used = 149946368 (143.0MB) free = 187695104 (179.0MB) 44.409937888198755% used Survivor Space: regions = 1 capacity = 1048576 (1.0MB) used = 1048576 (1.0MB) free = 0 (0.0MB) 100.0% used G1 Old Generation: regions = 43 capacity = 198180864 (189.0MB) used = 43203520 (41.20208740234375MB) free = 154977344 (147.79791259765625MB) 21.800046244626323% used 26479 interned Strings occupying 2650792 bytes.
2.2. 查看对象占用的内存 -histo
查看taskManager Runner进程下所有(活)对象占用的内存
jps |grep Task //12756 TaskManagerRunner jmap -histo 12756 > histo.log
num #instances #bytes class name ---------------------------------------------- 1: 38301 9046568 [B 2: 359468 8627232 akka.dispatch.AbstractNodeQueue$Node 3: 326220 7829280 akka.actor.LightArrayRevolverScheduler$TaskQueue 4: 84323 5411784 [C 5: 11166 2986648 [I 6: 58 1901472 [Lakka.dispatch.forkjoin.ForkJoinTask; 7: 52856 1268544 java.lang.String 8: 20054 1081096 [Ljava.lang.Object; 9: 8837 983192 java.lang.Class 10: 36268 870432 java.lang.Long 11: 35157 843768 java.util.concurrent.ConcurrentLinkedQueue$Node 12: 34639 831336 akka.dispatch.Envelope 13: 33877 813048 scala.concurrent.duration.FiniteDuration 14: 33247 797928 akka.actor.LightArrayRevolverScheduler$TaskHolder 15: 33230 797520 akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask 16: 33230 797520 akka.dispatch.TaskInvocation 17: 17377 556064 java.util.concurrent.ConcurrentHashMap$Node 18: 21209 339344 akka.remote.EndpointWriter$$anonfun$akka$remote$EndpointWriter$$trySendPureAck$1 19: 8959 286688 java.io.ObjectStreamClass$WeakClassKey 20: 4335 277440 java.nio.DirectByteBuffer 21: 6704 268160 java.util.HashMap$KeyIterator 22: 7372 235904 java.util.HashMap$Node 23: 4096 229376 org.apache.flink.core.memory.HybridMemorySegment 24: 2077 216008 java.io.ObjectStreamClass 25: 3246 207744 scala.collection.immutable.VectorIterator 26: 12542 200672 java.lang.Object 27: 4112 164480 sun.misc.Cleaner 28: 6494 155856 scala.PartialFunction$$anonfun$runWith$1 29: 6494 155856 scala.collection.mutable.MapBuilder 30: 1740 131624 [Ljava.util.HashMap$Node; 31: 4108 131456 java.nio.DirectByteBuffer$Deallocator 32: 4 131136 [Ljava.util.concurrent.ForkJoinTask; 33: 3627 116064 scala.collection.immutable.HashMap$HashMap1 34: 110 113168 [Ljava.util.concurrent.ConcurrentHashMap$Node; 35: 4597 110328 java.lang.StringBuilder 36: 1205 106040 java.lang.reflect.Method 37: 2174 104352 java.util.HashMap 38: 6494 103904 scala.collection.TraversableLike$$anonfun$collect$1 39: 6494 103904 scala.collection.generic.GenMapFactory$MapCanBuildFrom 40: 3038 94872 [Ljava.lang.String;
由于jmap将访问堆中的所有对象,为了保证在此过程中不被应用线程干扰,jmap需要借助安全点机制,让所有线程停留在不改变堆中数据的状态。也就是说,由jmap导出的堆快照必定是安全点位置的。这可能导致基于该对快照的分析结果存在偏差。
例如:假设在编译生成的机器码中,某些堆对象的生命周期在两个安全点之间,那么:live选项将无法探知到这些对象。
另外,如果某个线程长时间无法跑到安全点,jmap将一直等下去。与jstat不同的是,垃圾回收器会主动将jstat所需要的摘要数据保存至固定位置之中,而jstat只需直接读取即可。
2.3. 查看进程的类加载器
jmap -clstats 171266 Attaching to process ID 171266, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.152-b16 finding class loader instances ..done. computing per loader stat ..done. please wait.. computing liveness.liveness analysis may be inaccurate ... class_loader classes bytes parent_loader alive? type2511 4281349 null live 0x00000000ff02eb00 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e03bd838 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e1ce29c8 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000feb01ab8 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e08b2880 1 1471 null dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000ff44ff48 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e1b699b8 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e1ce15c0 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000fffe03f0 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e233fe20 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e1b69da0 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e1ce11d8 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000feb132a8 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e1cd1dd0 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e1cdf1d0 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e1ce4dd0 315 736975 0x00000000e03bca00 dead org/apache/flink/runtime/execution/librarycache/FlinkUserCodeClassLoaders$SafetyNetWrapperClassLoader@0x0000000100821e50 0x00000000e1ce69d0 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e1cdc1e8 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000e1cdede8 1 1471 0x00000000e03bca00 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 。。。 total = 386 14889 24886849 N/A alive=1, dead=385 N/A
线程处于死锁状态,将占用系统大量资源。
Wait on condition它在等待另一个条件的发生来唤醒自己,或者自己调用了sleep()方法。
状态通常是WAITING (parking)(等待唤醒条件)或 TIMED_WAITING (parking或sleeping)(等待定时唤醒条件)。
Waiting for Monitor Entry and in Object.wait()如果大量线程处于此种状态,说明这些线程又去获取第三方资源了,比如第三方的网络资源或读取数据库的操作,长时间无法获得响应,导致大量线程进入等待状态。因此,这说明系统处于一个网络瓶颈或读取数据库操作时间太长。
等待进入临界区,处于block状态。
blocked如果大量线程处于这种状态的话,可能是一个全局锁阻塞了大量线程。
如果短期内多次打印Thread Dump信息,发现 waiting for monitor entry 状态的线程越来越多,没有减少的趋势,可能意味着某些线程在临界区里呆得时间太长了,以至于越来越多新线程迟迟无法进入。
线程处于阻塞状态,需要根据实际情况进行判断。



