4.1 jps
jps(Java Virtual Machine Process Status Tool)是JDK 1.5提供的一个显示当前所有java进程pid的命令,简单实用,非常适合在linux/unix平台上简单察看当前java进程的一些简单情况。
jps存放在JAVA_HOME/bin/jps,使用时为了方便请将JAVA_HOME/bin/加入到Path.
$> jps 23991 Jps 23789 BossMain 23651 Resin |
比较常用的参数:
-l 输出应用程序main class的完整package名 或者 应用程序的jar文件完整路径名
$> jps -l 28729 sun.tools.jps.Jps 23789 com.asiainfo.aimc.bossbi.BossMain 23651 com.caucho.server.resin.Resin |
-v 输出传递给JVM的参数
$> jps -v 23789 BossMain 28802 Jps -Denv.class.path=/data/aoxj/bossbi/twsecurity/java/trustwork140.jar:/data/aoxj/bossbi/twsecurity/java/:/data/aoxj/bossbi/twsecurity/java/twcmcc.jar:/data/aoxj/jdk15/lib/rt.jar:/data/aoxj/jdk15/lib/tools.jar -Dapplication.home=/data/aoxj/jdk15 -Xms8m 23651 Resin -Xss1m -Dresin.home=/data/aoxj/resin -Dserver.root=/data/aoxj/resin -Djava.util.logging.manager=com.caucho.log.LogManagerImpl -Djavax.management.builder.initial=com.caucho.jmx.MBeanServerBuilderImpl |
-m 输出传递给main 方法的参数,在嵌入式jvm上可能是null
$> jps -m 28715 Jps -m 23789 BossMain 23651 Resin -socketwait 32768 -stdout /data/aoxj/resin/log/stdout.log -stderr /data/aoxj/resin/log/stderr.log |
这些命令也可以联合使用,如jps -lmv
$> jps -lmv 25611 sun.tools.jps.Jps -lmv -Denv.class.path=.:/usr/local/jdk8/lib/rt.jar:/home/tianshouzhi/download/clojure-1.7.0/clojure-1.7.0.jar -Dapplication.home=/usr/local/jdk8 -Xms8m 13789 website-0.0.1-SNAPSHOT.jar -Xms128m -Xmx512m -Xmn128m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:+DisableExplicitGC -XX:-OmitStackTraceInFastThrow -Djava.ext.dirs=/usr/local/jdk8/jre/lib/ext |
注:jps命令有个地方很不好,似乎只能显示当前用户的java进程,要显示其他用户的还是只能用unix/linux的ps命令。
更多用法:请参考sun官方文档
http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jps.html
4.2 jinfo
jinfo用于观察、修改运行中的java程序的运行环境参数:参数包括Java System属性和JVM命令行参数。
jinfo语法
jinfo [option] <pid> |
其中<option> 是以下之一:
-flag <name> 打印指定name的 JVM flag
-flag [+|-]<name> 启用或者禁用指定name的 JVM flag
-flag <name>=<value> 设置指定名称的name flag 为给定的值
-flags 打印 JVM flags
-sysprops 打印Java 系统属性
<no option> 不指定任何option,则打印出所有的JVM flags和sysprops
-h | -help 打印帮助信息
观察所有的java系统属性和非默认的JVM Flags
[root@www wangxiaoxiao]# jinfo 15525 Attaching to process ID 15525, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.65-b01 Java System Properties: jboss.i18n.generate-proxies = true java.runtime.name = Java(TM) SE Runtime Environment java.vm.version = 25.65-b01 sun.boot.library.path = /usr/local/jdk8/jre/lib/amd64 java.protocol.handler.pkgs = null|org.springframework.boot.loader java.vendor.url = http://java.oracle.com/ java.vm.vendor = Oracle Corporation path.separator = : file.encoding.pkg = sun.io java.vm.name = Java HotSpot(TM) 64-Bit Server VM sun.os.patch.level = unknown sun.java.launcher = SUN_STANDARD user.country = US user.dir = /data/website java.vm.specification.name = Java Virtual Machine Specification PID = 15525 java.runtime.version = 1.8.0_65-b17 java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment os.arch = amd64 java.endorsed.dirs = /usr/local/jdk8/jre/lib/endorsed org.jboss.logging.provider = slf4j line.separator = java.io.tmpdir = /tmp java.vm.specification.vendor = Oracle Corporation os.name = Linux sun.jnu.encoding = UTF-8 java.library.path = /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib spring.beaninfo.ignore = true java.specification.name = Java Platform API Specification java.class.version = 52.0 sun.management.compiler = HotSpot 64-Bit Tiered Compilers os.version = 2.6.32-431.23.3.el6.x86_64 user.home = /root user.timezone = Asia/Harbin catalina.useNaming = false java.awt.printerjob = sun.print.PSPrinterJob file.encoding = UTF-8 java.specification.version = 1.8 catalina.home = /tmp/tomcat.4517928592901920395.8081 user.name = root java.class.path = website-0.0.1-SNAPSHOT.jar java.vm.specification.version = 1.8 sun.arch.data.model = 64 sun.java.command = website-0.0.1-SNAPSHOT.jar java.home = /usr/local/jdk8/jre user.language = en java.specification.vendor = Oracle Corporation awt.toolkit = sun.awt.X11.XToolkit java.vm.info = mixed mode java.version = 1.8.0_65 java.ext.dirs = /usr/local/jdk8/jre/lib/ext sun.boot.class.path = /usr/local/jdk8/jre/lib/resources.jar:/usr/local/jdk8/jre/lib/rt.jar:/usr/local/jdk8/jre/lib/sunrsasign.jar:/usr/local/jdk8/jre/lib/jsse.jar:/usr/local/jdk8/jre/lib/jce.jar:/usr/local/jdk8/jre/lib/charsets.jar:/usr/local/jdk8/jre/lib/jfr.jar:/usr/local/jdk8/jre/classes java.awt.headless = true java.vendor = Oracle Corporation catalina.base = /tmp/tomcat.4517928592901920395.8081 file.separator = / LOG_EXCEPTION_CONVERSION_WORD = %wEx java.vendor.url.bug = http://bugreport.sun.com/bugreport/ sun.io.unicode.encoding = UnicodeLittle sun.cpu.endian = little sun.cpu.isalist = VM Flags: Non-default VM flags: -XX:CICompilerCount=2 -XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:+DisableExplicitGC -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=536870912 -XX:MaxNewSize=134217728 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=134152192 -XX:OldPLABSize=16 -XX:OldSize=65536 -XX:-OmitStackTraceInFastThrow -XX:SoftRefLRUPolicyMSPerMB=0 -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC Command line: -Xms128m -Xmx512m -Xmn128m -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:+DisableExplicitGC -XX:-OmitStackTraceInFastThrow -Djava.ext.dirs=/usr/local/jdk8/jre/lib/ext |
注意,这里的VM Flags只打印出非默认的参数,对于默认的参数如何查看,请参考:http://ifeve.com/useful-jvm-flags-part-3-printing-all-xx-flags-and-their-values/
修改运行中的JVM Flags
jinfo还能够修改一部分运行期间能够调整的虚拟机参数。例如系统启动时,并没有指定这样的参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/java/dump.hprof,这两个参数用于内存dump分析,后面讲解jmap的时候会介绍。
则可以通过jinfo命令进行修改:
jinfo -flag +HeapDumpOnOutOfMemoryError 15525 jinfo -flag HeapDumpPath=/java/dump.hprof 15525 |
注意:
1、如果运行过程中,通过jinfo修改了,则修改后的值只能通过jinfo看到,jps是看不到的,jps命令只能看到启动时的jvm参数。
2、很多运行参数是不能调整的,如果出现这种异常,说明不能调整:
[root@www wangxiaoxiao]# jinfo -flag SurvivorRatio=7 15525
Exception in thread "main" com.sun.tools.attach.AttachOperationFailedException: flag 'SurvivorRatio' cannot be changed
最后,对于运行时的修改,jinfo命令好像存在一个bug,如下图所示,我们明明设置了HeapDumpPath=/java/dump.hprof,但是这里显示的却是null:
但是直接指定查看HeapDumpPath这个flag时,返回的又是我们设置的值。
4.3 jmap与jhat
jmap(Java Memory Map)命令可以获得运行中的jvm的堆的快照,从而可以离线分析堆,以检查内存泄漏,检查一些严重影响性能的大对象的创建,检查系统中什么对象最多,各种对象所占内存的大小。
命令语法:
jmap [option] <pid> |
其中pid可以通过jps命令获取,option可选项如下:
-heap:打印jvm 内存整体使用情况
-histo[:live]:打印jvm heap的直方图。其输出信息包括类名,对象数量,对象占用大小。
-dump:<dump-options>:dump java内存到二进制文件中。
dump-options:
live 是否只dump当前存活的对象,如果不指定,将会dump所有的对象,包括待回收的对象。
format=b 文件的格式
file=<file> 指定文件的输出位置
-permstat:打印permanent generation heap情况
上述命令中的-histo和-dumo都包含一个live选项,如果指定live,在统计前会进行full gc,因此不加live的堆大小要大于加live堆的大小。
命令使用
1、打印jvm 内存整体使用情况
jmap -heap pid
可以观察到New Generation(Eden Space,From Space,To Space),tenured generation,Perm Generation的内存使用情况
[root@www wangxiaoxiao]# jmap -heap 12446 Attaching to process ID 12446, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.65-b01 using parallel threads in the new generation. using thread-local object allocation. Concurrent Mark-Sweep GC Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 536870912 (512.0MB) NewSize = 134152192 (127.9375MB) MaxNewSize = 134217728 (128.0MB) OldSize = 65536 (0.0625MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: New Generation (Eden + 1 Survivor Space): capacity = 120782848 (115.1875MB) used = 80598816 (76.86502075195312MB) free = 40184032 (38.322479248046875MB) 66.73034899789745% used Eden Space: capacity = 107413504 (102.4375MB) used = 75663840 (72.15866088867188MB) free = 31749664 (30.278839111328125MB) 70.44164577295606% used From Space: capacity = 13369344 (12.75MB) used = 4934976 (4.70635986328125MB) free = 8434368 (8.04364013671875MB) 36.91262637867647% used To Space: capacity = 13369344 (12.75MB) used = 0 (0.0MB) free = 13369344 (12.75MB) 0.0% used concurrent mark-sweep generation: capacity = 198795264 (189.5859375MB) used = 138417264 (132.00498962402344MB) free = 60378000 (57.58094787597656MB) 69.62804908672271% used 44470 interned Strings occupying 5924248 bytes. |
2、查看类名,对象数量,对象占用大小
jmap -histo[:live] pid
可以观察heap中所有对象的情况(heap中所有生存的对象的情况)。包括对象数量和所占空间大小。
[root@www wangxiaoxiao]# jmap -histo 12446 | more num #instances #bytes class name ---------------------------------------------- 1: 648278 81563800 [C 2: 252059 30068112 [B 3: 502067 12049608 java.lang.String 4: 53315 8825728 [I 5: 114705 8823824 [Ljava.lang.Object; 6: 210107 6723424 java.util.HashMap$Node 7: 60821 5352248 java.lang.reflect.Method 8: 62414 5060664 [S 9: 54688 4812544 java.util.jar.JarEntry 10: 131308 4201856 org.springframework.boot.loader.util.AsciiBytes 11: 28337 3420512 [Ljava.util.HashMap$Node; 12: 59001 3304056 org.springframework.boot.loader.jar.JarEntryData 13: 87337 2794784 java.util.concurrent.ConcurrentHashMap$Node 14: 69859 2794360 java.lang.ref.SoftReference 15: 36298 2613456 java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask 16: 22683 2497536 java.lang.Class 17: 60280 2411200 java.util.LinkedHashMap$Entry 18: 27206 1741184 java.net.URL 19: 56643 1359432 java.util.concurrent.ConcurrentSkipListMap$Node 20: 22687 1270472 java.util.LinkedHashMap 21: 23984 1151232 java.util.HashMap 22: 15122 1088784 org.springframework.boot.loader.jar.JarURLConnection 23: 26521 1060840 java.util.TreeMap$Entry 24: 57292 916672 java.lang.Object 25: 27960 894720 java.util.Hashtable$Entry 26: 36242 869808 java.util.concurrent.Executors$RunnableAdapter 27: 931 838160 [Ljava.util.concurrent.ConcurrentHashMap$Node; |
输出说明:
instances列:表示当前类有多少个实例。
bytes列:说明当前类的实例总共占用了多少个字节
class name列:表示的就是当前类的名称,class name 解读:
B代表byte
C代表char
D代表double
F代表float
I代表int
J代表long
Z代表boolean
前边有[代表数组,[I 就相当于int[]
对象用[L+类名表示
手工dump内存到本地文件
jmap -dump:file=heap.map 12446
[root@www wangxiaoxiao]# jmap -dump:file=heap.map 12446 Dumping heap to /home/wangxiaoxiao/heap.map ... Heap dump file created |
jhat(Java Heap Analyse Tool)
用途:是用来分析java堆的命令,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言
使用:
[root@www wangxiaoxiao]# jhat heap.map #heap.map是通过jmap -dump:file=heap.map pid命令导出 Reading from heap.map... Dump file created Tue Feb 14 10:50:34 CST 2017 Snapshot read, resolving... Resolving 2570075 objects... Chasing references, expect 514 dots ........................................................................................................................... Eliminating duplicate references ............................................................................................................................. Snapshot resolved. Started HTTP server on port 7000 Server is ready. |
访问 http://localhost:7000,就可以查看详细的内存信息
关于jhat,不打算多说,因为一般实际开发中,很少使用jhat来直接对内存dump文件进行分析。下面介绍一个MAT,这个用的比较多。
MAT(Memory Analyzer Tool)
MAT是一个eclipse的插件,上手起来比较快。它能够快速的分析dump文件,可以直观的看到各个对象在内存占用的量大小,以及类实例的数量,对象之间的引用关系,找出对象的GC Roots相关的信息,此外还能生成内存泄露报表,疑似泄露大对象的报表等等。
安装MAT
可以选择eclipse插件的方式安装 http://download.eclipse.org/mat/1.3/update-site/ 也可以选择单独MAT程序下载安装 http://www.eclipse.org/mat/downloads.php |
内存溢出时,自动保存dump文件
前面是手工导出内存dump映射文件,如果应用已经在线上运行,为了能获取应用出现内存溢出时获得heap dump文件,以便在之后分析,可以在JVM启动时指定参数:-XX:+HeapDumpOnOutOfMemoryError,JVM会在遇到OutOfMemoryError时保存一个“堆转储快照”,并将其保存在一个文件中。 文件路径通过-XX:HeapDumpPath指定。
案例
public class OomDemo {
public static void main(String[] args) {
Map<String,Long> map=new HashMap<String, Long>();
for (long i = 0; i < Long.MAX_VALUE; i++) {
map.put(i+"",i);
}
}
}
设置虚拟机参数为:-Xmx40m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\Java\dump.hprof
执行程序,很快会抛出异常:
java.lang.OutOfMemoryError: GC overhead limit exceeded Dumping heap to D:\Java\dump ... Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded Heap dump file created [55940678 bytes in 0.556 secs] at java.lang.StringBuilder.toString(StringBuilder.java:405) at oom.OomDemo.main(OomDemo.java:13) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) |
生成了文件之后,就可以通过MAT打开来进行分析
File->Open Heap Dump->选择文件
弹出如下界面:
选中第一个,点击finish,出现以下界面:
可以看到,提示在主线程(main)的一个本地变量中使用了98.77%的内存,而这个本地变量就是java.util.HashMap$Entry[]。
4.4 jstat
jstat是JDK自带的一个轻量级小工具。全称“Java Virtual Machine statistics monitoring tool”,它位于java的bin目录下,主要利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。
jstat语法
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]] |
jstat支持的option通过jstat -optioins进行查看
[root@www wangxiaoxiao]# jstat -options -class 统计class loader行为信息 ,例如当前总共加载了多少个类 -compile 统计HotSpot Just-in-Time compiler的行为 -gc 统计jdk gc时heap信息 -gccapacity 统计不同的generations相应的heap容量情况 -gccause 统计gc的情况,(同-gcutil)和引起gc的事件 -gcnew 统计gc时,新生代的情况 -gcnewcapacity 统计gc时,新生代heap容量 -gcold 统计gc时,老年区的情况 -gcoldcapacity 统计gc时,老年区heap容量 -gcpermcapacity 统计gc时,permanent区heap容量 -gcutil 统计gc时,heap情况 -printcompilation hotspot编译方法统计 |
其中:
-gc综合了-gcnew、-gcold的输出;
-gccapacity综合了-gcnewcapacity、-gcoldcapacity、-gcpermcapacity的输出
统计JVM gc状况
参数说明:
S0C 当前survivor space 0 的总容量 (KB).
S1C 当前survivor space 1 的总容量 (KB).
S0U 当前survivor space 0 已使用的容量 (KB).
S1U 当前survivor space 1 已使用的容量 (KB).
EC 当前 eden space 总容量 (KB).
EU 当前eden space已经使用的容量 (KB).
OC 当前 old space 总容量 (KB).
OU 当前old space 已使用容量(KB).
PC 当前 permanent space 总容量(KB).
PU 当前 permanent space 已使用容量 (KB).
YGC 从应用启动时到现在,年轻代young generation 发生GC Events的总次数.
YGCT 从应用启动时到现在, 年轻代Young generation 垃圾回收的总耗时.
FGC 从应用启动时到现在, full GC事件总次数.
FGCT 从应用启动时到现在, Full sc总耗时.
GCT 从应用启动时到现在, 垃圾回收总时间. GCT=YGCT+FGCT
上图说明了,总应用启动到现在,YGC总共发生了136次,总耗时YGCT=6.891秒,FGC发生了20次,总耗时FGCT=1.384秒,FGC+YGC总耗时GCT=8.275秒。
可以发现五条记录YGC、YGCT、YGC、YGCT、GCT值都是相同的,说明在我们统计的这段时间(每1秒统计1次,共5次),所以这5秒内没有发生gc事件。
此外,这里显示的S0C、S1C、EC、OC、PC显示的值,与jmap中都有相对应的条目,为了进行说明,我们通过jmap
命令与这里的参数进行对比:
jmap -heap 15525 ... Heap Usage: New Generation (Eden + 1 Survivor Space): capacity = 120782848 (115.1875MB) used = 54471592 (51.948158264160156MB) free = 66311256 (63.239341735839844MB) 45.09878091299851% used Eden Space: capacity = 107413504 (102.4375MB) #对应EC used = 51631152 (49.23930358886719MB) #对应EU free = 55782352 (53.19819641113281MB) 48.06765451018151% used From Space: capacity = 13369344 (12.75MB) #对应S0C used = 2840440 (2.7088546752929688MB) #对应S0U free = 10528904 (10.041145324707031MB) 21.24591902190564% used To Space: capacity = 13369344 (12.75MB) #对应S1C used = 0 (0.0MB) #对应S1U free = 13369344 (12.75MB) 0.0% used concurrent mark-sweep generation: #老年代使用了cms收集器 capacity = 194482176 (185.47265625MB) #对应OC used = 123681224 (117.95160675048828MB) #对应OU free = 70800952 (67.52104949951172MB) 63.59514611765759% used |
注意在jstat中显示的值以kb为单位,乘以1024之后,就是通过jmap -heap命令显示对应的字节数。
4.5 jstack
jstack命令主要用于调试java程序运行过程中的线程堆栈信息,可以用于检测死锁,进程耗用cpu过高报警问题的排查。
jstack语法格式:
[root@www wangxiaoxiao]# jstack Usage: jstack [-l] <pid> jstack -F [-m] [-l] <pid> Options: -F 强制dump线程堆栈信息. 用于进程hung住, jstack <pid>命令没有响应的情况 -m 同时打印java和本地(native)线程栈信息,m是mixed mode的简写 -l 打印锁的额外信息 |
jstack命令会打印出所有的线程,包括用户自己启动的线程和jvm后台线程,我们主要关注的是用户线程,如
[root@www wangxiaoxiao]# jstack 15525 2017-02-14 21:10:02 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.65-b01 mixed mode): "elasticsearch[Native][merge][T#1]" #98 daemon prio=5 os_prio=0 tid=0x00007f031c009000 nid=0x4129 waiting on condition [0x00007f02f61ee000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000000eea589f0> (a org.elasticsearch.common.util.concurrent.EsExecutors$ExecutorScalingQueue) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.LinkedTransferQueue.awaitMatch(LinkedTransferQueue.java:737) at java.util.concurrent.LinkedTransferQueue.xfer(LinkedTransferQueue.java:647) at java.util.concurrent.LinkedTransferQueue.take(LinkedTransferQueue.java:1269) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) .... |
线程dump信息说明:
elasticsearch[Native][merge][T#1] 是我们为线程起的名字
daemon 表示线程是否是守护线程
prio 表示我们为线程设置的优先级
os_prio 表示的对应的操作系统线程的优先级,由于并不是所有的操作系统都支持线程优先级,所以可能会出现都置为0的情况
tid 是java中为这个线程的id
nid 是这个线程对应的操作系统本地线程id,每一个java线程都有一个对应的操作系统线程
wait on condition表示当前线程处于等待状态,但是并没列出具体原因
java.lang.Thread.State: WAITING (parking) 也是表示的处于等待状态,括号中的内容说明了导致等待的原因,例如这里的parking说明是因为调用了 LockSupport.park方法导致等待
java.lang.Thread.State说明:
一个Thread对象可以有多个状态,在java.lang.Thread.State
中,总共定义六种状态:
1、NEW
线程刚刚被创建,也就是已经new过了,但是还没有调用start()方法,jstack命令不会列出处于此状态的线程信息
2、RUNNABLE #java.lang.Thread.State: RUNNABLE
RUNNABLE这个名字很具有欺骗性,很容易让人误以为处于这个状态的线程正在运行。事实上,这个状态只是表示,线程是可运行的。我们已经无数次提到过,一个单核CPU在同一时刻,只能运行一个线程。
3、BLOCKED # java.lang.Thread.State: BLOCKED (on object monitor)
线程处于阻塞状态,正在等待一个monitor lock。通常情况下,是因为本线程与其他线程公用了一个锁。其他在线程正在使用这个锁进入某个synchronized同步方法块或者方法,而本线程进入这个同步代码块也需要这个锁,最终导致本线程处于阻塞状态。
4、WAITING
等待状态,调用以下方法可能会导致一个线程处于等待状态:
-
Object.wait
不指定超时时间 # java.lang.Thread.State: WAITING (on object monitor)
Thread.join
with no timeout
LockSupport.park #java.lang.Thread.State: WAITING (parking)
例如:对于wait()方法,一个线程处于等待状态,通常是在等待其他线程完成某个操作。本线程调用某个对象的wait()方法,其他线程处于完成之后,调用同一个对象的notify或者notifyAll()方法。Object.wait()方法只能够在同步代码块中调用。调用了wait()方法后,会释放锁。
5、TIMED_WAITING
线程等待指定的时间,对于以下方法的调用,可能会导致线程处于这个状态:
Thread.sleep #java.lang.Thread.State: TIMED_WAITING (sleeping)
Object.wait
指定超时时间 #java.lang.Thread.State: TIMED_WAITING (on object monitor)
Thread.join
with timeout
LockSupport.parkNanos #java.lang.Thread.State: TIMED_WAITING (parking)
LockSupport.parkUntil #java.lang.Thread.State: TIMED_WAITING (parking)
6、TERMINATED
线程终止。
说明,对于 java.lang.Thread.State: WAITING (on object monitor)和java.lang.Thread.State: TIMED_WAITING (on object monitor),对于这两个状态,是因为调用了Object的wait方法(前者没有指定超时,后者指定了超时),由于wait方法肯定要在syncronized代码中编写,因此肯定是如类似以下代码导致:
synchronized(obj) {
.........
obj.wait();
.........
}
因此通常的堆栈信息中,必然后一个lock标记,如下:
"CM1" #21 daemon prio=5 os_prio=0 tid=0x00007f02f0d6d800 nid=0x3d48 in Object.wait() [0x00007f02fefef000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:502) at java.util.TimerThread.mainLoop(Timer.java:526) - locked <0x00000000eca75aa8> (a java.util.TaskQueue) at java.util.TimerThread.run(Timer.java:505) |
关于死锁
在 JAVA 5中加强了对死锁的检测。线程 Dump中可以直接报告出 Java级别的死锁,如下所示:
Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x0003f334 (object 0x22c19f18, a java.lang.Object), which is held by "Thread-0" "Thread-0": waiting to lock monitor 0x0003f314 (object 0x22c19f20, a java.lang.Object), which is held by "Thread-1" |
关于nid
每个线程都有一个tid 和nid,tid是java中这个线程的编号,而nid(native id)是对应操作系统线程id。有的时候,我们会收到报警,说服务器,某个进程占用CPU过高,肯定是因为某个java线程有耗CPU资源的方法。
可以通过命令cat /proc/<pid>/task
查看一个进程下面有哪些线程
可以通过"top -Hp
"命令查看这个进程内所有线程的cpu和内容使用情况使用情况:
上例中看到12446进程中,12467线程占用cpu最高,是0.3%。要想看这个线程对应java哪个线程,首先用以下命令将12467转成16进制:
接着使用jstack命令,显示堆栈信息:
4.6 jconsole与jvisualvm
jvisualvm同jconsole都是一个基于图形化界面的、可以查看本地及远程的JAVA GUI监控工具,可以认为jvisualvm是jconsole的升级版,因此这里不再介绍jconsole,只介绍jvisualvm。jvisualvm是一个综合性的分析工具,可以认为其整合了jstack、jmap、jinfo等众多调试工具的功能,并以图形界面展示.
jvisualvm启动很简单,直接在命令行中输入"jvisualvm"即可。之后出现下图:
侧边框介绍:
本地:如果你本地有java进程启动了,那么在本地这个栏目就会显示。
远程:就是监控的远程主机
由于本地和远程展示的监控界面都是相同的,这里直接介绍远程,远程监控回了,本地监控自然而然也会了。
注意,一个主机如果希望支持远程监控,需要在启动时添加以下参数:
-Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false |
此外,-Dcom.sun.management.jmxremote JDK5时,需要指定这个参数,开启JMX管理功能,JDK6之后,JMX管理默认开启,不需要指定
之后,右击"远程"-->"添加远程主机",出现界面
在连接后面添加一个1099,这是远程主机jmx监听的端口号,点击确定,侧边栏变为:
点击红色框中的jmx连接,出现以下界面
jvisualvm分为四个选项卡:概述、监视、线程、抽样器,下面我们一一介绍:
“概述 ”选项卡:
默认显示的就是概述选项卡,其中的信息相当于我们调用了jinfo命令获得,其还包含了两个子选项卡:
jvm参数栏:相当于我们调用jinfo -flags <pid>获得
系统属性栏:相当于我们调用jinfo -sysprops <pid>获得
“监视”选项卡:
主要显示了cpu、内存使用、类加载信息、线程信息等,这只是一个概要性的介绍,如下图:
右上角的"堆dump"会在远程主机上,dump一个内存映射文件,之所以不直接dump到本地,主要是因为这个文件通常比较大,直接dump到本地会很慢。
dump完成之后,可以手工下载这个文件,通过"文件"->"装入"来进行分析,不过我们一般还是使用mat来进行分析,不会使用这个功能。
“线程”选项卡:
线程选项卡列出了所有线程的信息,并使用了不同的颜色标记,右下角的颜色表示了不同的状态。
右上角的线程dump会直接把线程信息dump到本地,相当于调用了jstack命令,如:
“抽样器 ”选项卡:
主要有"cpu"和"内存"两个按钮,读者可以分别点击一下,看一下显示效果。