查看Java进程:jps
用法介绍
jps命令:显示所有进程号和短的类名称
Jps –q 命令:只显示进程号
Jps –l 用于传输主函数的完整路径
Jps –v 显示传递给Java虚拟机的参数(感觉这个命令才是完美,把虚拟机的一些参数全都打印出来)
查看线程堆栈命令:jstack命令
Jstack命令主要用来查看Java线程的调用堆栈的,可以用来分析线程问题(如死锁)。谈到线程,在Java里面,线程一共有6中状态
- New 新建 ————- 不会出现在dump中
- Runnable 正在运行中——–在虚拟机内执行
- Blocked 阻塞————受阻塞,并等待监视器锁
- Waiting 等待————无限期等待另一个线程执行特定操作
- Timed_waiting 超时等待————有时限等待另一个线程的操作
- Terminated 终止/结束————已退出的
Monitor
在多线程的 JAVA程序中,实现线程之间的同步,就要说说 Monitor。 Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。下 面这个图,描述了线程和 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()”。 先看 “Entry Set”里面的线程。我们称被 synchronized保护起来的代码段为临界区。当一个线程申请进入临界区时,它就进入了 “Entry Set”队列。对应的 code就像:
synchronized(obj) {
.........
}
- 1
- 2
- 3
- 4
调用修饰
表示线程在方法调用时,额外的重要的操作。线程Dump分析的重要信息。修饰上方的方法调用。
locked <地址> 目标:使用synchronized申请对象锁成功,监视器的拥有者。
waiting to lock <地址> 目标:使用synchronized申请对象锁未成功,在迚入区等待。
waiting on <地址> 目标:使用synchronized申请对象锁成功后,释放锁幵在等待区等待。
parking to wait for <地址> 目标
locked
at oracle.jdbc.driver.PhysicalConnection.prepareStatement
- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)
at oracle.jdbc.driver.PhysicalConnection.prepareStatement
- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)
at com.jiuqi.dna.core.internal.db.datasource.PooledConnection.prepareStatement
- 1
- 2
- 3
- 4
- 5
- 6
通过synchronized关键字,成功获取到了对象的锁,成为监视器的拥有者,在临界区内操作。对象锁是可以线程重入的。
waiting to lock
at com.jiuqi.dna.core.impl.CacheHolder.isVisibleIn(CacheHolder.java:165)
- waiting to lock <0x0000000097ba9aa8> (a CacheHolder)
at com.jiuqi.dna.core.impl.CacheGroup$Index.findHolder
at com.jiuqi.dna.core.impl.ContextImpl.find
at com.jiuqi.dna.bap.basedata.common.util.BaseDataCenter.findInfo
- 1
- 2
- 3
- 4
- 5
通过synchronized关键字,没有获取到了对象的锁,线程在监视器的进入区等待。在调用栈顶出现,线程状态为Blocked。
waiting on
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000da2defb0> (a WorkingThread)
at com.jiuqi.dna.core.impl.WorkingManager.getWorkToDo
- locked <0x00000000da2defb0> (a WorkingThread)
at com.jiuqi.dna.core.impl.WorkingThread.run
- 1
- 2
- 3
- 4
- 5
- 6
通过synchronized关键字,成功获取到了对象的锁后,调用了wait方法,进入对象的等待区等待。在调用栈顶出现,线程状态为WAITING或TIMED_WATING。
parking to wait for
park是基本的线程阻塞原语,不通过监视器在对象上阻塞。随concurrent包会出现的新的机制,不synchronized体系不同。
线程动作
线程状态产生的原因
runnable:状态一般为RUNNABLE。
in Object.wait():等待区等待,状态为WAITING或TIMED_WAITING。
waiting for monitor entry:进入区等待,状态为BLOCKED。
waiting on condition:等待区等待、被park。
sleeping:休眠的线程,调用了Thread.sleep()。
Wait on condition 该状态出现在线程等待某个条件的发生。具体是什么原因,可以结合 stacktrace来分析。 最常见的情况就是线程处于sleep状态,等待被唤醒。 常见的情况还有等待网络IO:在java引入nio之前,对于每个网络连接,都有一个对应的线程来处理网络的读写操作,即使没有可读写的数据,线程仍然阻塞在读写操作上,这样有可能造成资源浪费,而且给操作系统的线程调度也带来压力。在 NewIO里采用了新的机制,编写的服务器程序的性能和可扩展性都得到提高。 正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。一种情况是网络非常忙,几 乎消耗了所有的带宽,仍然有大量数据等待网络读 写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。所以要结合系统的一些性能观察工具来综合分析,比如 netstat统计单位时间的发送包的数目,如果很明显超过了所在网络带宽的限制 ; 观察 cpu的利用率,如果系统态的 CPU时间,相对于用户态的 CPU时间比例较高;如果程序运行在 Solaris 10平台上,可以用 dtrace工具看系统调用的情况,如果观察到 read/write的系统调用的次数或者运行时间遥遥领先;这些都指向由于网络带宽所限导致的网络瓶颈。(来自http://www.blogjava.net/jzone/articles/303979.html)
jstack 命令详解
简单介绍:
F当’jstack [-l] pid’没有相应的时候强制打印栈信息 -l长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表. -m打印java和native c/c++框架的所有栈信息. -h | -help打印帮助信息 pid 需要被打印配置信息的java进程id,可以用jps查询.
第一个实战代码:
/**
* Created by Cser_W on 2018/7/10.
*/
public class JstackDemo {
public static void main(String[] args){
while (true) {
//do nothing
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
先利用 jps 查看进程号
利用jstack 进程号查看线程堆栈信息,如果发现自己写的代码一直处于Runnable状态,这有很大可能是自己写了个死循环。
第二个实战代码
/**
* Created by Cser_W on 2018/7/10.
*/
public class JstackDemo1 {
public static void main(String[] args){
Thread thread = new Thread(new Thread1());
thread.start();
}
}
class Thread1 extends Thread {
@Override
public void run(){
while (true) {
System.out.println(1);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
我们能看到:
线程的状态: WAITING 线程的调用栈 线程的当前锁住的资源: < < <0x00000000da380ee0>>> 线程当前等待的资源:< < <0x00000000da380ee0>>>
为什么同时锁住的等待同一个资源:
线程的执行中,先获得了这个对象的 Monitor(对应于 locked < <0x00000000da380ee0>>)。当执行到 obj.wait(), 线程即放弃了 Monitor的所有权,进入 “wait set”队列(对应于 waiting on < <0x00000000da380ee0>> )。
死锁模拟实战
package com.wxy.test;
/**
* Created by Cser_W on 2018/7/10.
*/
public class JstackDemo2 {
public static void main(String[] args){
Thread thread1 = new Thread(new DeadLockClass(true));
Thread thread2 = new Thread((new DeadLockClass(false)));
thread1.start();
thread2.start();
}
}
class DeadLockClass implements Runnable {
public boolean flag;
DeadLockClass(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
while (true) {
synchronized (Suo.o1) {
System.out.println("o1" + Thread.currentThread().getName());
synchronized (Suo.o2) {
System.out.println("o2" + Thread.currentThread().getName());
}
}
}
} else {
while (true) {
synchronized (Suo.o2) {
System.out.println("o2" + Thread.currentThread().getName());
synchronized (Suo.o1) {
System.out.println("o1" + Thread.currentThread().getName());
}
}
}
}
}
}
class Suo {
static Object o1 = new Object();
static Object o2 = new Object();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
运行输出:
上图已经锁死,只要两个线程都启动起来,必定会发生死锁。这个时候赶紧拿jstack练手了
用jstack命令显示:
打印内存映射,制作堆Dump命令:Jmap
堆map的概述
堆Dump是反应Java堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。 一般,在内存不足、GC异常等情况下,我们就会怀疑有内存泄露。这个时候我们就可以制作堆Dump来查看具体情况
用法摘要
Usage:
jmap [option] <pid>
(to connect to running process)
jmap [option] <executable <core>
(to connect to a core file)
jmap [option] [server_id@]<remote server IP or hostname>
(to connect to remote debug server)
where <option> is one of:
<none> to print same info as Solaris pmap
-heap to print java heap summary
-histo[:live] to print histogram of java object heap; if the "live"
suboption is specified, only count live objects
-permstat to print permanent generation statistics
-finalizerinfo to print information on objects awaiting finalization
-dump:<dump-options> to dump java heap in hprof binary format
dump-options:
live dump only live objects; if not specified,
all objects in the heap are dumped.
format=b binary format
file=<file> dump heap to <file>
Example: jmap -dump:live,format=b,file=heap.bin <pid>
-F force. Use with -dump:<dump-options> <pid> or -histo
to force a heap dump or histogram when <pid> does not
respond. The "live" suboption is not supported
in this mode.
-h | -help to print this help message
-J<flag> to pass <flag> directly to the runtime system
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
指定进程号(pid)的进程 jmap [ option ] 指定核心文件 jmap [ option ] 指定远程调试服务器jmap [ option ] [server-id@]
参数:
option 选项参数是互斥的(不可同时使用)。想要使用选项参数,直接跟在命令名称后即可。
pid 需要打印配置信息的进程ID。该进程必须是一个Java进程。想要获取运行的Java进程列表,你可以使用jps。
executable 产生核心dump的Java可执行文件。
core 需要打印配置信息的核心文件。
remote-hostname-or-IP 远程调试服务器的(请查看jsadebugd)主机名或IP地址。
server-id 可选的唯一id,如果相同的远程主机上运行了多台调试服务器,用此选项参数标识服务器。
选项:
如果使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。
-dump:[live,]format=b,file= 以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。如果指定了live子选项,堆中只有活动的对象会被转储。想要浏览heap dump,你可以使用jhat(Java堆分析工具)读取生成的文件。
-finalizerinfo 打印等待终结的对象信息。
-heap 打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和generation wise heap usage。
-histo[:live] 打印堆的柱状图。其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个’*’前缀。如果指定了live子选项,则只计算活动的对象。
-permstat 打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。
-F 强制模式。如果指定的pid没有响应,请使用jmap -dump或jmap -histo选项。此模式下,不支持live子选项。
-h 打印帮助信息。
-help 打印帮助信息。
-J 指定传递给运行jmap的JVM的参数。
查看java 堆(heap)使用情况,执行命令:
Jmap –heap pid
查看堆内存(histogram)中的对象数量及大小。执行命令:
Jmap –histo pid
总结:
1. 如果程序内存不足或者频繁GC,很有可能存在内存泄露情况,这时候就要借助Java堆Dump查看对象的情况。
2.要制作堆Dump可以直接使用jvm自带的jmap命令
3.可以先使用jmap -heap命令查看堆的使用情况,看一下各个堆空间的占用情况。
4.使用jmap -histo:[live]查看堆内存中的对象的情况。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。
5.也可以使用 jmap -dump:format=b,file=命令将堆信息保存到一个文件中,再借助jhat命令查看详细内容
6.在内存出现泄露、溢出或者其它前提条件下,建议多dump几次内存,把内存文件进行编号归档,便于后续内存整理分析。
性能监控工具命令:jstat
用法讲解
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
- 1
参数解释:
Option — 选项,我们一般使用 -gcutil 查看gc情况
vmid — VM的进程号,即当前运行的java进程号
interval– 间隔时间,单位为秒或者毫秒
count — 打印次数,如果缺省则打印无数次
参数 interval 和 count 代表查询间隔和次数,如果省略这两个参数,说明只查询一次。
示例:
Jstat –gc 4100 250 5
上图中参数的意思:
S0C 年轻代中第一个survivor(幸存区)的容量 (字节)
S0U 年轻代中第一个survivor(幸存区)目前已使用空间 (字节)
EC 年轻代中Eden(伊甸园)的容量 (字节)
EU 年轻代中Eden(伊甸园)目前已使用空间 (字节)
OU Old代目前已使用空间 (字节)
PC Perm(持久代)的容量 (字节)
PU Perm(持久代)目前已使用空间 (字节)
YGC 从应用程序启动到采样时年轻代中gc次数
FGC 从应用程序启动到采样时old代(全gc)gc次数
FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT 从应用程序启动到采样时gc用的总时间(s)
Jstat –class 4100 250 5 显示加载class的数量,及所占空间等信息。
Loaded 装载的类的数量
Unloaded 卸载类的数量
Bytes 卸载类的字节数
Time 装载和卸载类所花费的时间
jstat -compiler 显示VM实时编译的数量等信息
Compiled 编译任务执行数量
Invalid 编译任务执行失效数量
Time 编译任务消耗时间
FailedType 最后一个编译失败任务的类型
FailedMethod 最后一个编译失败任务所在的类及方法
Jstat –gccapacity 4100
NGCMN 年轻代(young)中初始化(最小)的大小(字节)
NGC 年轻代(young)中当前的容量 (字节)
S0C 年轻代中第一个survivor(幸存区)的容量 (字节)
S1C 年轻代中第二个survivor(幸存区)的容量 (字节)
EC 年轻代中Eden(伊甸园)的容量 (字节)
OGCMN old代中初始化(最小)的大小 (字节)
OGCMX old代的最大容量(字节)
OGC old代当前新生成的容量 (字节)
OC Old代的容量 (字节)
PGCMN perm代中初始化(最小)的大小 (字节)
PGCMX perm代的最大容量 (字节)
PGC perm代当前新生成的容量 (字节)
PC Perm(持久代)的容量 (字节)
YGC 从应用程序启动到采样时年轻代中gc次数
FGC 从应用程序启动到采样时old代(全gc)gc次数
参考资料
【Java命令学习系列(二)-Jstack】http://www.hollischuang.com/archives/110
【Java命令学习系列(三)—Jmap】http://www.hollischuang.com/archives/303
【Java命令学习系列(四)—jstat】http://www.hollischuang.com/archives/481