Java监控工具、调优、调试辅助函数
Java的安装包自带了很多优秀的工具,善用这些工具对于监控和调试Java程序非常有帮助。常用工具如下:
用途:jps用来查看JVM里面所有进程的具体状态,?包括进程ID,进程启动的路径等等。
常用参数:
-l: 输出完成的包名称;
-m: 输出程序的命令行输入参数;
-v: 输出完整的JVM参数。
用途:1)查看java程序崩溃生成core文件,获得core文件的java?stack和native?stack的信息;2)查看正在运行的java程序的java?stack和native?stack的信息:a) 查看运行的java程序呈现hung的状态;b) 跟踪Java的调用栈,剖析程序。
用途:jinfo可观察运行中的java程序的运行环境参数:参数包括Java?System属性和JVM命令行参数;也可从core文件里面知道崩溃的Java应用程序的配置信息。
用途:jstat利用了JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap?size和垃圾回收状况的监控等等。
语法结构:
Usage:?jstat?-help|-options
???????jstat?-<option>?[-t]?[-h<lines>]?<vmid>?[<interval>?[<count>]]
参数解释:
Options?—?选项,我们一般使用?-gcutil?查看gc情况
vmid????—?VM的进程号,即当前运行的java进程号
interval–?间隔时间,单位为秒或者毫秒
count???—?打印次数,如果缺省则打印无数次
具体option参数如下:
-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情况
输出内容含义如下:
S0??—?Heap上的?Survivor?space?0?区已使用空间的百分比
S1??—?Heap上的?Survivor?space?1?区已使用空间的百分比
E???—?Heap上的?Eden?space?区已使用空间的百分比
O???—?Heap上的?Old?space?区已使用空间的百分比
P???—?Perm?space?区已使用空间的百分比
YGC?—?从应用程序启动到采样时发生?Young?GC?的次数
YGCT–?从应用程序启动到采样时?Young?GC?所用的时间(单位秒)
FGC?—?从应用程序启动到采样时发生?Full?GC?的次数
FGCT–?从应用程序启动到采样时?Full?GC?所用的时间(单位秒)
GCT?—?从应用程序启动到采样时用于垃圾回收的总时间(单位秒)?
示例
实例使用1:
[root@localhost?bin]#?jstat?-gcutil?25444
??S0?????S1?????E??????O??????P?????YGC?????YGCT????FGC????FGCT?????GCT
?11.63???0.00???56.46??66.92??98.49?162????0.248????6??????0.331????0.579?
实例使用2:
[root@localhost?bin]#?jstat?-gcutil?25444?1000?5
??S0?????S1?????E??????O??????P?????YGC?????YGCT????FGC????FGCT?????GCT
?73.54???0.00??99.04??67.52??98.49????166????0.252?????6????0.331????0.583
?73.54???0.00??99.04??67.52??98.49????166????0.252?????6????0.331????0.583
?73.54???0.00??99.04??67.52??98.49????166????0.252?????6????0.331????0.583
?73.54???0.00??99.04??67.52??98.49????166????0.252?????6????0.331????0.583
?73.54???0.00??99.04??67.52??98.49????166????0.252?????6????0.331????0.583?
我们可以看到,5次young?gc之后,垃圾内存被从Eden?space区(E)放入了Old?space区(O),并引起了百分比的变化,导致Survivor?space使用的百分比从73.54%(S0)降到0%(S1)。有效释放了内存空间。绿框中,我们可以看到,一次full?gc之后,Old?space区(O)的内存被回收,从99.05%降到67.52%。
图中同时打印了young?gc和full?gc的总次数、总耗时。而,每次young?gc消耗的时间,可以用相间隔的两行YGCT相减得到。每次full?gc消耗的时间,可以用相隔的两行FGCT相减得到。例如红框中表示的第一行、第二行之间发生了1次young?gc,消耗的时间为0.252-0.252=0.0秒。
常驻内存区(P)的使用率,始终停留在98.49%左右,说明常驻内存没有突变,比较正常。
如果young?gc和full?gc能够正常发生,而且都能有效回收内存,常驻内存区变化不明显,则说明java内存释放情况正常,垃圾回收及时,java内存泄露的几率就会大大降低。但也不能说明一定没有内存泄露。
GCT?是YGCT?和FGCT的时间总和。
以上,介绍了Jstat按百分比查看gc情况的功能。其实,它还有功能,例如加载类信息统计功能、内存池信息统计功能等,那些是以绝对值的形式打印出来的,比较少用,在此就不做介绍。?
[root@localhost?bin]#?ps?-ef?|?grep?java
root?????25917?????1??2?23:23?pts/2????00:00:05?/usr/local/jdk1.5/bin/java?-Djava.endorsed.dirs=/usr/local/jakarta-tomcat-5.0.30/common/endorsed?-classpath?/usr/local/jdk1.5/lib/tools.jar:/usr/local/jakarta-tomcat-5.0.30/bin/bootstrap.jar:/usr/local/jakarta-tomcat-5.0.30/bin/commons-logging-api.jar?-Dcatalina.base=/usr/local/jakarta-tomcat-5.0.30?-Dcatalina.home=/usr/local/jakarta-tomcat-5.0.30?-Djava.io.tmpdir=/usr/local/jakarta-tomcat-5.0.30/temp?org.apache.catalina.startup.Bootstrap?start
jstat?-class?pid:显示加载class的数量,及所占空间等信息。
实例使用3:
[root@localhost?bin]#?jstat?-class?25917
Loaded??Bytes??Unloaded??Bytes?????Time
2629????2916.8???????29???24.6?????0.90?
jstat?-compiler?pid:显示VM实时编译的数量等信息。
实例使用4:
[root@localhost?bin]#?jstat?-compiler?25917
Compiled?Failed?Invalid???Time???FailedType?FailedMethod
?????768??????0???????0???0.70????????????0?
jstat?–gccapacity?:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推,?OC是old内纯的占用量。?
[root@localhost?bin]#?jstat?-gccapacity?25917
NGCMN???????640.0
NGCMX???????4992.0
NGC?????????832.0
S0C?????????64.0
S1C?????????64.0
EC??????????704.0
OGCMN???????1408.0
OGCMX???????60544.0
OGC?????????9504.0
OC??????????9504.0??????????????????OC是old内纯的占用量
PGCMN???????8192.0??????????????????PGCMN显示的是最小perm的内存使用量
PGCMX???????65536.0?????????????????PGCMX显示的是perm的内存最大使用量
PGC?????????12800.0?????????????????PGC是当前新生成的perm内存占用量
PC??????????12800.0?????????????????PC是但前perm内存占用量
YGC?????????164
FGC?????????6?
jstat?-gcnew?pid:?new对象的信息
[root@localhost?bin]#?jstat?-gcnew?25917
?S0C????S1C????S0U????S1U???TT?MTT??DSS??????EC???????EU?????YGC?????YGCT
?64.0???64.0???47.4???0.0???2??15???32.0????704.0????145.7????168????0.254?
jstat?-gcnewcapacity?pid:?new对象的信息及其占用量
[root@localhost?bin]#?jstat?-gcnewcapacity?25917
?NGCMN??NGCMX???NGC???S0CMX??S0C???S1CMX??S1C???ECMX????EC??????YGC???FGC
640.0??4992.0??832.0?64.0???448.0?448.0??64.0???4096.0??704.0??168?????6
jstat?-gcold?pid:?old对象的信息。
[root@localhost?bin]#?jstat?-gcold?25917
???PC???????PU????????OC??????????OU???????YGC????FGC????FGCT?????GCT
?12800.0??12617.6?????9504.0??????6561.3???169?????6????0.335????0.591
jstat?-gcoldcapacity?pid:old对象的信息及其占用量。
[root@localhost?bin]#?jstat?-gcoldcapacity?25917
OGCMN??????OGCMX????????OGC?????????OC???????YGC???FGC????FGCT?????GCT
1408.0?????60544.0??????9504.0??????9504.0???169?????6????0.335????0.591?
jstat?-gcpermcapacity?pid:?perm对象的信息及其占用量。
[root@localhost?bin]#?jstat?-gcpermcapacity?25917
PGCMN??????PGCMX???????PGC?????????PC??????YGC???FGC????FGCT?????GCT
8192.0????65536.0????12800.0????12800.0???169?????6????0.335????0.591?
jstat?-printcompilation?pid:当前VM执行的信息。
[root@localhost?bin]#?jstat?-printcompilation?-h3??25917?1000?5
每1000毫秒打印一次,一共打印5次,还可以加上-h3每三行显示一下标题。
Compiled??Size??Type?Method
?????788?????73????1?java/io/File?<init>
?????788?????73????1?java/io/File?<init>
?????788?????73????1?java/io/File?<init>
Compiled??Size??Type?Method
?????788?????73????1?java/io/File?<init>
?????788?????73????1?java/io/File?<init>
用途:观察运行中的jvm物理内存的占用情况,包括Heap?size,?Perm?size等等。
参数如下:
-heap:打印jvm?heap的情况
-histo:打印jvm?heap的直方图。其输出信息包括类名,对象数量,对象占用大小。
-histo:live?:同上,但是只答应存活对象的情况
-permstat:打印permanent?generation?heap情况
命令使用:
jmap?-heap?2083 ----?观察到New?Generation(Eden?Space,From?Space,To?Space),tenured?generation,Perm?Generation的内存使用情况
jmap?-histo?2083?|?jmap?-histo:live?2083 ? ----?观察heap中所有对象的情况(heap中所有生存的对象的情况)。包括对象数量和所占空间大小。
jmap -dump:live,format=b,file=heap.bin 2083 ----?dump java heap in hprof binary format。输出文件可用于进一步分析。
JVM调优总结?
JAVA虚拟机性能参数调优指导书
Jprofiler使用介绍
Memory Analyzer (MAT)
使用Memory Analyzer tool(MAT)分析内存泄漏
调试Java程序经常需要知道Java进程PID、当前目录、Java Home、内存使用情况、执行特定命令等等。JavaDebug收集了上述这些常用的辅助调试函数。
?
import java.lang.management.ManagementFactory;import java.lang.management.RuntimeMXBean;import java.io.File;import java.io.IOException;import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.util.Iterator;import java.util.Map;import java.util.Properties;public class JavaDebug{ public static int getPid() { RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); String name = runtime.getName(); // format: "pid@hostname" try { return Integer.parseInt(name.substring(0, name.indexOf('@'))); } catch (Exception e) { System.err.println(e.getMessage()); return -1; } } public static void printEnv(){ Map<String,String> map = System.getenv(); Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator(); Map.Entry<String, String> entry; while (iter.hasNext()) { entry = iter.next(); String name = entry.getKey(); String value = entry.getValue(); System.out.println(name + ": "+ value); } } public static void printProperties(){ Properties pros = System.getProperties(); pros.list(System.out); } public static String getPWD(){ return System.getProperty("user.dir"); } public static void printSimpleMemoryInfo(){ Runtime runTime = Runtime.getRuntime(); System.out.println("Total Mem:" + runTime.totalMemory() + " Max Mem:" +runTime.maxMemory() + " Free mem:" + runTime.freeMemory() ); } public static void printMemoryInfo(String info){ if( info != null){ System.out.println(info+ " JavaDebug:printMemoryInfo"); } printSimpleMemoryInfo(); ExecCommand("jmap " + getPid()); } public static void ExecCommand(String cmd){ System.out.println("Exec command: "+cmd); try{ Process pro = null; Runtime runTime = Runtime.getRuntime(); if (runTime == null) { System.err.println("Create runtime false!"); throw new RuntimeException("Create runtime false!"); } pro = runTime.exec(cmd); BufferedReader input = new BufferedReader(new InputStreamReader(pro.getInputStream())); PrintWriter output = new PrintWriter(new OutputStreamWriter(pro.getOutputStream())); String line; while ((line = input.readLine()) != null) { System.out.println(line); } input.close(); output.close(); pro.destroy(); } catch(IOException e) { System.err.println(e.getMessage()); e.printStackTrace(); throw new RuntimeException("run command error!"); } System.out.println("Exec command: "+cmd + " finish."); } public static void main(String[] args) throws Exception { printProperties(); //printEnv(); System.out.println("========================"); int pid = getPid(); System.out.println("pid: " + pid); System.out.println("pwd: " + getPWD()); printMemoryInfo(null); ExecCommand("ls -l"); ExecCommand("jmap " + pid); printMemoryInfo(null); }}?