一、What
JDK 本身提供了很多很方便的 JVM 性能调优工具,如 VisualVM、Jconsole、jps、jstack、jmap、jhat、Jstat、jprof等。
可以帮我们解决下列问题:
- OutofMemory 内存不足
- 内存泄漏
- 线程死锁
- 锁争用(Lock Contention)
- java 进程消耗 CPU 过高
1. Jstack(查看线程)
1.1 作用:
jstack主要用来抓取 Java JVM 中 某个进程 某一时刻 线程堆栈信息。其实就是抓取 thread dump 文件。
thread dump: 诊断 java 应用问题的工具,可以显示 Java JVM中的所有线程在某一个时间点的快照文件。
1.2 内容:
线程标识,运行状态,调用的堆栈、调用的堆栈包含完整的类名、所执行的方法、还有源代码的行数。
Sun JVM 常见线程状态:
Runnable(R): 当前可以运行的线程。
Waiting on monitor(CW): 线程主动 wait
Waiting for monitor entry(MW):线程等锁
JVM 中的 thin lock, fat lock, spin lock, tasuki lock:
1.3 Demo:
"process reaper" daemon prio=10 tid=0x00007f3d7014b800 nid=0x8b80a waiting on condition [0x00007f3dc68f0000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000f007cfb0> (a java.util.concurrent.SynchronousQueue$TransferStack)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:359)
at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:942)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
process reaper: 线程名称
daemon: 线程类型,守护线程
prio: 线程优先级
tid: thread id,JVM 中线程唯一标识
nid: native thread id,系统中线程唯一标识
waiting on condition: 线程状态
at…: 堆栈信息
Jstack 的目的主要是抓取 thread dump 文件然后使用工具进行分析。
1.4 常见线程:
Attach Listener:负责接收外部命令,用户第一次执行 JVM 命令的时候得到启动。
Signal Dispather:Attach Listener 接收到外部命令交给 Signal Dispather 进行分发到各个不同的模块处理,并且返回处理结果。
CompilerThread0:实时编译卸载 class,JVM 会启动多个线程来处理这部分工作,线程名称后面的数字也会累加。
Concurrent Mark-Sweep GC Thread:并发标记清除垃圾回收器线程(CMC GC),该线程主要针对老年代垃圾回收
Finalizer线程:垃圾收集前,调用对象的finalize()方法
- 只有当开始一轮垃圾收集时,才会开始调用finalize()方法;因此并不是所有对象的finalize()方法都会被执行
- 该线程也是daemon线程,因此如果虚拟机中没有其他非daemon线程,不管该线程有没有执行完finalize()方法,JVM也会退出
- JVM在垃圾收集时会将失去引用的对象包装成Finalizer对象(Reference的实现),并放入ReferenceQueue,由Finalizer线程来处理;最后将该Finalizer对象的引用置为null,由垃圾收集器来回收
- JVM为什么要单独用一个线程来执行finalize()方法呢?如果JVM的垃圾收集线程自己来做,很有可能由于在finalize()方法中误操作导致GC线程停止或不可控,这对GC线程来说是一种灾难
2. Jstat(查看性能)
2.1 作用:
可以观察到classloader,compiler,gc相关信息。可以实时监控资源和性能 。
2.2 命令选项:
-class:统计class loader行为信息
-compile:统计编译行为信息
-gc:统计jdk gc时heap信息
-gccapacity: 统计不同的generations(不知道怎么翻译好,包括新生区,老年区,permanent区)相应的heap容量情况
-gccause:统计gc的情况,(同-gcutil)和引起gc的事件
-gcnew:统计gc时,新生代的情况
-gcnewcapacity:统计gc时,新生代heap容量
-gcold:统计gc时,老年区的情况
-gcoldcapacity:统计gc时,老年区heap容量
-gcpermcapacity:统计gc时,permanent区heap容量
-gcutil:统计gc时,heap情况
2.3 参数内容
S0 — Heap上的 Survivor space 0 区已使用空间的百分比
S0C:S0当前容量的大小
S0U:S0已经使用的大小
S1 — Heap上的 Survivor space 1 区已使用空间的百分比
S1C:S1当前容量的大小
S1U:S1已经使用的大小
E — Heap上的 Eden space 区已使用空间的百分比
EC:Eden space当前容量的大小
EU:Eden space已经使用的大小
O — Heap上的 Old space 区已使用空间的百分比
OC:Old space当前容量的大小
OU:Old space已经使用的大小
P — Perm space 区已使用空间的百分比
OC:Perm space当前容量的大小
OU:Perm space已经使用的大小
YGC — 从应用程序启动到采样时发生 Young GC 的次数
YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)
FGC — 从应用程序启动到采样时发生 Full GC 的次数
FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒),它的值等于YGC+FGC
3. Jmap(查看内存)
3.1 作用:
jmap 用来查看堆内存使用状况,一般结合 jhat 使用
3.2 命令选项
-heap:打印 heap 的概要信息,GC 使用的算法,heap 的配置以及 wise heap 的使用情况。
-histo:打印每个 class 的实例数目,内存占用,类全名信息,
-permstat:打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来.
二、Why
有时候内存泄漏,内存溢出,或者死锁等现象出现的时候仅仅通过应用程序的日志信息是很难定位问题的,很多时候这种情况出现的时候我们的解决方案都是重启服务器,但是这种方式并没有从根本上解决问题,程序运行一段时间还是会出现上述现象。
所以为了从根本上解决这些问题,我们首先要定位问题,知道问题发生的原因,因为 Java 应用程序都是运行在 JVM 上的,所以了解 JVM 的概念和原理是我们发现问题的第一步。
JVM 中为我们提供了一些内置的工具可以用于抓取 JVM 的状态信息,比如使用 Jstack 可以抓取当前进程的堆栈信息,分析后判断是否存在死锁问题,使用 Jmap 可以抓取堆内存信息,分析后可以发现各种内存泄漏,内存溢出的原因,使用 Jstat 可以查看系统的性能等。
三、How
1. Jstack
操作流程:
第一步:抓取堆栈信息写入 a.txt
jstack –l <pid>
第二步:使用工具分析堆栈信息
2. Jstat
抓取信息:
# jstat -gc 30077 100 5
3. Jmap
使用 IBM 的HeapAnalyzer 分析堆内存信息。
第一步:抓取数据
第二步:使用工具分析:HeapAnalyzer
四、参考资料
【1】性能分析之-- JAVA Thread Dump 分析综述
http://blog.csdn.net/rachel_luo/article/details/8920596
【2】jstack(查看线程)、jmap(查看内存)和jstat(性能分析)命令
http://guafei.iteye.com/blog/1815222
【3】三个实例演示 Java Thread Dump 日志分析
http://www.cnblogs.com/zhengyun_ustc/archive/2013/01/06/dumpanalysis.html
【4】关于JVM的Thin Lock, Fat Lock, SPIN Lock与Tasuki Lock
【5】JVM性能调优监控工具