在JDK的bin目彔下,包含了java命令及其他实用工具
- jps:查看本机的Java中进程信息
- jstack:打印线程的栈信息,制作线程Dump
- jmap:打印内存映射,制作堆Dump
- jstat:性能监控工具
- jhat:内存分析工具
- jconsole:简易的可视化控制台
- jvisualvm:功能强大的控制台
jps(查看Java进程 )
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jps -help
usage: jps [-help]
jps [-q] [-mlvV] [<hostid>]Definitions:
<hostid>: <hostname>[:<port>]
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jps -l
31860 ./sds-cloud-user-base-service.jar
8853 ./sds-cloud-push-service.jar
30920 ./sds-cloud-user-ccu-service.jar
21518 sun.tools.jps.Jps
20063 ./sds-cloud-third-device-service.jar
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jps -v…内容省略
jstack(查看线程堆栈命令 )
Jstack命令主要用来查看Java线程的调用堆栈的,可以用来分析线程问题(如死锁)。谈到线程,在Java里面,线程一共有6中状态:
- New 新建 ————- 不会出现在dump中
- Runnable 正在运行中——–在虚拟机内执行
- Blocked 阻塞————受阻塞,并等待监视器锁
- Waiting 等待————无限期等待另一个线程执行特定操作
- Timed_waiting 超时等待————有时限等待另一个线程的操作
- Terminated 终止/结束————已退出的
命令:
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstack -help
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F [-m] [-l] <pid>
(to connect to a hung process)
jstack [-m] [-l] <executable> <core>
(to connect to a core file)
jstack [-m] [-l] [server_id@]<remote server IP or hostname>
(to connect to a remote debug server)Options:
-F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
-m to print both java and native frames (mixed mode)
-l long listing. Prints additional information about locks
-h or -help to print this help message
F当’jstack [-l] pid’没有相应的时候强制打印栈信息 -l长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表. -m打印java和native c/c++框架的所有栈信息. -h | -help打印帮助信息 pid 需要被打印配置信息的java进程id,可以用jps查询。
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) {
.........
}
调用修饰
表示线程在方法调用时,额外的重要的操作。线程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
通过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
通过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
通过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的系统调用的次数或者运行时间遥遥领先;这些都指向由于网络带宽所限导致的网络瓶颈。
jmap(打印内存映射,制作堆Dump命令 )
堆Dump是反应Java堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。 一般,在内存不足、GC异常等情况下,我们就会怀疑有内存泄露。这个时候我们就可以制作堆Dump来查看具体情况
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jmap -help 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 -clstats to print class loader 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
查看java 堆(heap)使用情况,执行命令: jmap -heap PID
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jmap -heap 30920 Attaching to process ID 30920, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.101-b13 using thread-local object allocation. Parallel GC with 4 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 1073741824 (1024.0MB) NewSize = 357564416 (341.0MB) MaxNewSize = 357564416 (341.0MB) OldSize = 716177408 (683.0MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 328204288 (313.0MB) used = 2758584 (2.6307907104492188MB) free = 325445704 (310.3692092895508MB) 0.8405082142010284% used From Space: capacity = 14680064 (14.0MB) used = 10102176 (9.634185791015625MB) free = 4577888 (4.365814208984375MB) 68.81561279296875% used To Space: capacity = 14155776 (13.5MB) used = 0 (0.0MB) free = 14155776 (13.5MB) 0.0% used PS Old Generation capacity = 716177408 (683.0MB) used = 710051128 (677.1575241088867MB) free = 6126280 (5.842475891113281MB) 99.1445862531313% used 39726 interned Strings occupying 4021712 bytes.
查看堆内存(histogram)中的对象数量及大小,执行命令: jmap -histo PID
num #instances #bytes class name
———————————————-
1: 4222524 353956608 [C
2: 4144575 99469800 java.lang.String
3: 2834180 68020320 java.util.concurrent.LinkedBlockingQueue$Node
4: 2832427 67978248 com.google.common.eventbus.Subscriber$1
5: 262144 27262976 org.apache.logging.log4j.core.async.RingBufferLogEvent
6: 1048910 25173840 com.abc.a.b.y.event.MessageEvent
7: 891432 21394368 com.abc.a.b.y.event.BEvent
8: 891412 21393888 com.abc.a.b.y.event.FEvent
9: 27287 15690640 [B
10: 33354 11218744 [I
11: 87095 6710944 [Ljava.lang.Object;
12: 262144 6291456 org.apache.logging.log4j.core.time.MutableInstant
13: 178018 5696576 java.lang.StackTraceElement
14: 158477 5071264 java.util.concurrent.ConcurrentHashMap$Node
15: 667 3414248 [J
16: 35539 3127432 java.lang.reflect.Method
17: 19784 2185392 java.lang.Class
18: 51423 2056920 java.util.LinkedHashMap$Entry
19: 63533 2033056 java.util.HashMap$Node
查看堆内存存活中的对象数量及大小,执行命令: jmap -histo:live PID
num #instances #bytes class name
———————————————-
1: 4056916 328816032 [C
2: 4056458 97354992 java.lang.String
3: 2845541 68292984 java.util.concurrent.LinkedBlockingQueue$Node
4: 2845473 68291352 com.google.common.eventbus.Subscriber$1
5: 262144 27262976 org.apache.logging.log4j.core.async.RingBufferLogEvent
6: 1052249 25253976 com.a.b.c.u.c.event.MessageEvent
7: 896271 21510504 com.a.b.c.u.c.event.OEvent
8: 896251 21510024 com.a.b.c.u.c.event.FEvent
9: 11882 8395472 [B
10: 262144 6291456 org.apache.logging.log4j.core.time.MutableInstant
11: 151259 4840288 java.util.concurrent.ConcurrentHashMap$Node
12: 29130 3693576 [Ljava.lang.Object;
13: 35513 3125144 java.lang.reflect.Method
14: 573 2443960 [J
15: 19786 2185600 java.lang.Class
16: 9949 1606624 [I
17: 38121 1524840 java.util.LinkedHashMap$Entry
18: 1113 1465824 [Ljava.util.concurrent.ConcurrentHashMap$Node;
19: 29354 1408992 org.aspectj.weaver.reflect.ShadowMatchImpl
20: 14022 1157176 [Ljava.util.HashMap$Node;
21: 29354 939328 org.aspectj.weaver.patterns.ExposedState
22: 29256 936192 java.util.HashMap$Node
23: 14961 837816 java.util.LinkedHashMap
24: 47022 752352 java.lang.Object
25: 18355 507576 [Z
26: 22194 498824 [Ljava.lang.Class;
总结:
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命令可以查看堆内存各部分的使用量,以及加载类的数量。
命令的格式如下:jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数]
类加载统计:
jstat -class 30920
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstat -class 30920
Loaded Bytes Unloaded Bytes Time
18783 33700.4 185 250.1 19.85
- Loaded:加载class的数量
- Bytes:所占用空间大小
- Unloaded:未加载数量
- Bytes:未加载占用空间
- Time:时间
编译统计:
jstat -compiler 30920
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstat -compiler 30920
Compiled Failed Invalid Time FailedType FailedMethod
25712 1 0 169.94 1 sun/misc/URLClassPath getLoader
- Compiled:编译数量。
- Failed:失败数量
- Invalid:不可用数量
- Time:时间
- FailedType:失败类型
- FailedMethod:失败的方法
垃圾回收统计:
jstat -gc 30920
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstat -gc 30920
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
12800.0 12800.0 0.0 0.0 323584.0 157070.1 699392.0 693409.3 109644.0 102055.5 13656.0 12340.7 1056 32.676 6 7.075 39.751
- S0C:第一个幸存区的大小
- S1C:第二个幸存区的大小
- S0U:第一个幸存区的使用大小
- S1U:第二个幸存区的使用大小
- EC:伊甸园区的大小
- EU:伊甸园区的使用大小
- OC:老年代大小
- OU:老年代使用大小
- MC:方法区大小
- MU:方法区使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收消耗时间
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
堆内存统计:
jstat -gccapacity 30920
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstat -gccapacity 30920
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
349184.0 349184.0 349184.0 12800.0 12800.0 323584.0 699392.0 699392.0 699392.0 699392.0 0.0 1144832.0 109644.0 0.0 1048576.0 13656.0 1056
- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:当前新生代容量
- S0C:第一个幸存区大小
- S1C:第二个幸存区的大小
- EC:伊甸园区的大小
- OGCMN:老年代最小容量
- OGCMX:老年代最大容量
- OGC:当前老年代大小
- OC:当前老年代大小
- MCMN:最小元数据容量
- MCMX:最大元数据容量
- MC:当前元数据空间大小
- CCSMN:最小压缩类空间大小
- CCSMX:最大压缩类空间大小
- CCSC:当前压缩类空间大小
- YGC:年轻代gc次数
- FGC:老年代GC次数
新生代垃圾回收统计:
jstat -gcnew 30920
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstat -gcnew 30920
S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
14336.0 3072.0 0.0 2999.6 15 15 14336.0 323584.0 61696.9 1057 32.687
- S0C:第一个幸存区大小
- S1C:第二个幸存区的大小
- S0U:第一个幸存区的使用大小
- S1U:第二个幸存区的使用大小
- TT:对象在新生代存活的次数
- MTT:对象在新生代存活的最大次数
- DSS:期望的幸存区大小
- EC:伊甸园区的大小
- EU:伊甸园区的使用大小
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收消耗时间
新生代内存统计:
jstat -gcnewcapacity 30920
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstat -gcnewcapacity 30920
NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC
349184.0 349184.0 349184.0 116224.0 14336.0 116224.0 3072.0 348160.0 323584.0 1057 6
- NGCMN:新生代最小容量
- NGCMX:新生代最大容量
- NGC:当前新生代容量
- S0CMX:最大幸存1区大小
- S0C:当前幸存1区大小
- S1CMX:最大幸存2区大小
- S1C:当前幸存2区大小
- ECMX:最大伊甸园区大小
- EC:当前伊甸园区大小
- YGC:年轻代垃圾回收次数
- FGC:老年代回收次数
老年代垃圾回收统计:
jstat -gcold 30920
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstat -gcold 30920
MC MU CCSC CCSU OC OU YGC FGC FGCT GCT
109900.0 102068.1 13656.0 12340.7 699392.0 693409.3 1058 6 7.075 39.773
- MC:方法区大小
- MU:方法区使用大小
- CCSC:压缩类空间大小
- CCSU:压缩类空间使用大小
- OC:老年代大小
- OU:老年代使用大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
老年代内存统计:
jstat -gcoldcapacity 30920
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstat -gcoldcapacity 30920
OGCMN OGCMX OGC OC YGC FGC FGCT GCT
699392.0 699392.0 699392.0 699392.0 1059 6 7.075 39.786
- OGCMN:老年代最小容量
- OGCMX:老年代最大容量
- OGC:当前老年代大小
- OC:老年代大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
JDK8 下 元数据空间统计:
jstat -gcmetacapacity 30920
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstat -gcmetacapacity 30920
MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT
0.0 1144832.0 109900.0 0.0 1048576.0 13656.0 1059 6 7.075 39.786
- MCMN:最小元数据容量
- MCMX:最大元数据容量
- MC:当前元数据空间大小
- CCSMN:最小压缩类空间大小
- CCSMX:最大压缩类空间大小
- CCSC:当前压缩类空间大小
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
总结垃圾回收统计:
jstat -gcutil 30920
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstat -gcutil 30920
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 54.77 9.39 99.14 92.88 90.38 1061 32.739 6 7.075 39.814
- S0:幸存1区当前使用比例
- S1:幸存2区当前使用比例
- E:伊甸园区使用比例
- O:老年代使用比例
- M:元数据区使用比例
- CCS:压缩使用比例
- YGC:年轻代垃圾回收次数
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收消耗时间
- GCT:垃圾回收消耗总时间
VM编译方法统计:
jstat -printcompilation 30920
[root@iZbp1ii46cbd4er5cj3uw5Z sds-cloud]# jstat -printcompilation 30920
Compiled Size Type Method
25732 581 1 java/util/EnumSet of
- Compiled:最近编译方法的数量
- Size:最近编译方法的字节码数量
- Type:最近编译方法的编译类型
- Method:方法名标识