三,jhat
jhat用于分析产生的堆文件,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言。
1、导出堆文件:
jmap -dump:live,file=E:\heap.log pid
C:\Users\WWH>jmap -dump:live,file=E:\heap.log 18000
Dumping heap to E:\heap.log ...
Heap dump file created
2、分析堆文件:
jhat -J-Xmx512m 解析Java堆转储文件,并启动一个 web server;
说明:有时dump出来的堆文件很大,在启动时报堆空间不足的错误,可添加-J-Xmx512M参数;
jhat -J-Xmx512M D:\heap.log
C:\Users\WWH>jhat -J-Xmx512M E:\heap.log
Reading from E:\heap.log...
Dump file created Sun Jul 21 21:56:55 CST 2019
Snapshot read, resolving...
Resolving 8029 objects...
Chasing references, expect 1 dots.
Eliminating duplicate references.
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
3、查看html:在浏览器中输入主机地址:端口号(见上图红框框起部分):
说明:
All classes including platform 显示所有创建堆中对象的类
Show all members of the rootset 显示rootset能引用到的所有对象
Show instance counts for all classes (including platform) 显示所有类(包括JDK中定义的Java类)的实例数量
Show instance counts for all classes (excluding platform) 显示所有类(不包括JDK中定义的Java类)的实例数量
Show heap histogram 显示堆内对象直方图
Show finalizer summary 显示等待回收的对象信息
Execute Object Query Language (OQL) query 执行对象查询语句
四,jstack
jstack用于显示指定进程内线程的信息
option:命令选项,常用选项如下:
-F当’jstack [-l] pid’没有相应的时候强制打印栈信息,如果直接jstack无响应时,用于强制jstack),一般情况不需要使用
-l长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表,会使得JVM停顿得长久得多(可能会差很多倍,比如普通的jstack可能几毫秒和一次GC没区别,加了-l 就是近一秒的时间),-l 建议不要用。一般情况不需要使用
-m打印java和native c/c++框架的所有栈信息.可以打印JVM的堆栈,显示上Native的栈帧,一般应用排查不需要使用
实际操作:
C:\Users\WWH>jstack 18000
2019-07-21 22:19:22
Full thread dump Java HotSpot(TM) Client VM (25.131-b11 mixed mode):
"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x155e5400 nid=0x3208 runnable [0x00000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x15587000 nid=0x11c waiting on condition [0x00000000]
java.lang.Thread.State: RUNNABLE
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x155b4800 nid=0x34ac waiting on condition [0x00000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x155b3800 nid=0x1018 runnable [0x00000000]
java.lang.Thread.State: RUNNABLE
//daemon 表示线程是否是守护线程
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x1556b000 nid=0xcd8 in Object.wait() [0x158ff000]
//prio表示线程优先级,tid在java中表示线程编号,os_prio表示该线程对应操作系统的优先级
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0a557060> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x0a557060> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x15555000 nid=0x42d4 in Object.wait() [0x1586f000]
//tid即native id,该线程对应的操作系统中本地线程编号,每一个java线程都有一个对应的操作系统线程。
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0a555928> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x0a555928> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"main" #1 prio=5 os_prio=0 tid=0x02a4d800 nid=0x4394 runnable [0x02a9f000]
java.lang.Thread.State: RUNNABLE
at test.Test.main(Test.java:7)
//main是线程名, java.lang.Thread.State指线程状态
"VM Thread" os_prio=2 tid=0x02fee800 nid=0x304 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x15638c00 nid=0x3df8 waiting on condition
JNI global references: 8
线程与Monitor
进入区(Entrt Set):表示线程通过synchronized要求获取对象的锁。如果对象未被锁住,则迚入拥有者;否则则在进入区等待。一旦对象锁被其他线程释放,立即参与竞争。
拥有者(The Owner):表示某一线程成功竞争到对象锁。
等待区(Wait Set) :表示线程通过对象的wait方法,释放对象的锁,并在等待区等待被唤醒。
说明:
一个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程为“Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for monitor entry”,而在“Wait Set”中等待的线程状态是 “in Object.wait()”。 被 synchronized保护起来的代码段称为临界区,当一个线程申请进入临界区时,它就进入了 “Entry Set”队列。
线程状态:
NEW
线程刚刚被创建,也就是已经new过了,但是还没有调用start()方法,jstack命令不会列出处于此状态的线程信息。
RUNNABLE
RUNNABLE这个名字很具有欺骗性,很容易让人误以为处于这个状态的线程正在运行;事实上,这个状态只是表示,线程是可运行的。一个单核CPU在同一时刻,只能运行一个线程。
BLOCKED
线程处于阻塞状态,正在等待一个监视器锁(monitor lock)。通常情况下,是因为本线程与其他线程公用了一个锁。其他在线程正在使用这个锁进入某个synchronized同步方法块或者方法,而本线程进入这个同步代码块也需要这个锁,最终导致本线程处于阻塞状态;
WAITING
等待状态,等待某个condition或monitor发生,调用以下方法可能会导致一个线程处于等待状态:
wait() 不指定超时时间;
join() 不指定超时时间;
park() 例子;
TIMED_WAITING
线程等待指定的时间,对于以下方法的调用,可能会导致线程处于这个状态:
wait(long timeout) 指定超时时间
join(long millis) 指定超时时间
sleep(long millis) 指定超时时间;
parkNanos(long nanos);
parkUntil(long deadline);
TERMINATED
线程终止。