阅读JVM高级特性与最佳实践-2

jvm性能监控与故障处理

给一个系统定位问题,只是、经验是关键基础,数据是依据,工具是运用知识处理数据的手段。数据包括:运行日志、异常堆栈、GC日志、线程快照(threaddump/javacore文件)、堆转储快照(heapdump/hprof文件)等。工具只是知识技能的包装,不能根治百病,只能加快我们解决问题的步伐。

jdk命令行工具

在java安装目录下面,有很多17K左右的监控小工具,这些工具都是sun公司对安装目录下lib下tools工具包里面类的封装和使用

名称主要作用
jpsjvm process status tool,显示系统内所有的hotspot虚拟机进程
jstatjvm statistics monitoring tools,用于手机hotspot虚拟机各方面的运行数据
jinfoconfiguration info for java 显示虚拟机配置信息
jmapmemory map for java生成虚拟机内存转储快照heapdump文件
jhatjvm heap dump browser,用于分析heapdump文件,他会简历一个http/html服务器,让用户可以在浏览器上查看分析结果
jstackstack trace for java显示虚拟机的线程快照

jps

命令格式

jps [options] [hostid]

显示本地所有jvm进程

C:\Users\q>jps -l
7724 org/netbeans/Main visualvm
8304 sun.tools.jps.Jps jps本身
8732 我的eclipse
11172 com.omm.RuntimeConstantsPoolOOM 我跑的main程序

主要参数列表
- -q 只输出lvmid,省略主类的名称
- -m 输出虚拟机启动时传递给main函数的参数
- -l 输出朱磊的名称,如果执行的是jar包,则输出jar路径
- -v 输出虚拟机进程启动时jvm参数

尝试一下-v打印信息

C:\Users\q>jps -v
7724 Main -Xms24m -Xmx256m -Dsun.jvmstat.perdata.syncWaitMs=10000 -Dsun.java2d.n
oddraw=true -Dsun.java2d.d3d=false -Dnetbeans.keyring.no.master=true -Dplugin.ma
nager.install.global=false -Djdk.home=F:\worksoftware\Java8\jdk1874 -Dnetbeans.h
ome=F:\worksoftware\Java8\jdk1874\lib\visualvm\platform -Dnetbeans.user=C:\Users
\q\AppData\Roaming\VisualVM\8u40 -Dnetbeans.default_userdir_root=C:\Users\q\AppD
ata\Roaming\VisualVM -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\q
\AppData\Roaming\VisualVM\8u40\var\log\heapdump.hprof -Dsun.awt.keepWorkingSetOn
Minimize=true -Dnetbeans.dirs=F:\worksoftware\Java8\jdk1874\lib\visualvm\visualv
m;F:\worksoftware\Java8\jdk1874\lib\visualvm\profiler
8732  -Dosgi.requiredJavaVersion=1.7 -Xms256m -Xmx1024m  -Xbootclasspath/a:lombo
k.jar -javaagent:lombok.jar -XX:MaxPermSize=256m
10584 Jps -Dapplication.home=F:\worksoftware\java7\jdk7 -Xms8m

jstat

jstat,显示本地虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行行数据,命令格式

jstat [option vmid [interval[s|ms] [count]]]
vmid为进程IDinterval表示查询间隔,count表示查询次数

编写程序,让程序持续的进行垃圾回收,然后用jstat观察回收情况

//模拟程序
public class RuntimeConstantsPoolOOM {
    /**
     * -verbose:gc -XX:PermSize=10M -XX:MaxPermSize=10M -XX:+PrintGCDetails
     * @param args
     */
    public static void main(String[] args) {
        List<Thread> list = new ArrayList<>();
        while(true){
            Thread th = new Thread(new Runnable() {

                @Override
                public void run() {

                }
            });
            list.add(th);
            th.start();
        }
    }
}
//监测
C:\Users\q>jstat -gc 16064 1s 10
 S0C     S1C      S0U    S1U      EC       EU        OC         OU       PC       PU      YGC     YGCT  FGC    FGCT      GCT
28160.0 28672.0 17757.4  0.0   16896.0   4361.2   138240.0   78465.4   10240.0 2 508.2     14    0.189   2      0.452    0.641
28160.0 28672.0 17757.4  0.0   16896.0  12149.0   138240.0   78465.4   10240.0 2 508.2     14    0.189   2      0.452    0.641
30720.0 28672.0  0.0   25181.4 16896.0   2901.7   138240.0   78465.4   10240.0 2 508.2     15    0.211   2      0.452    0.663
30720.0 28672.0  0.0   25181.4 16896.0  10639.7   138240.0   78465.4   10240.0 2 508.2     15    0.211   2      0.452    0.663
30720.0 35328.0 30717.4  0.0   15872.0   1546.1   138240.0   80281.4   10240.0 2 508.2     16    0.235   2      0.452    0.687
30720.0 35328.0 30717.4  0.0   15872.0   9276.1   138240.0   80281.4   10240.0 2 508.2     16    0.235   2      0.452    0.687
37888.0 35328.0  0.0   29277.4 15872.0   1567.7   138240.0   88785.4   10240.0 2 508.2     17    0.258   2      0.452    0.710
37888.0 35328.0  0.0   29277.4 15872.0   9719.7   138240.0   88785.4   10240.0 2 508.2     17    0.258   2      0.452    0.710
37888.0 41984.0 36221.4  0.0   14336.0   1426.2   138240.0   88785.4   10240.0 2 508.2     18    0.279   2      0.452    0.732
37888.0 41984.0 36221.4  0.0   14336.0   9412.4   138240.0   88785.4   10240.0 2 508.2     18    0.279   2      0.452    0.732

S0C     S1C      S0U    S1U      EC       EU        OC         OU       PC       PU      YGC     YGCT  FGC    FGCT      GCT
s代表survivor  e代表eden   o 代表old  p代表permspace ygc代表年轻代GC  FGC代表full gc  c代表总量  u代表使用的量  t代表时间 

在没有桌面监控的情况下,jstat是非常好的监控选择,当然现在有很多提供监控平台的厂家,可以全天监控jvm的情况,jstat其他主要选项

-class 件事类装载、卸载数量、总空间以及类装载耗费的时间
-gc  件事java堆状况,包括eden区、两个survivor区、老年代、永久代的容量、已用容量、GC时间合计等信息
-gccapacity 监视内容与-gc基本相同,但输出主要关注java堆哥哥区域使用的最大、最小空间
-gcutil  监视内容与gc相同,但输出主要关注已经使用空间占空间的百分比
-gccause  与gcutil一样,会额外输出导致上一次GC产生的原因
-gcnew  监视新生代GC情况
-gcnewcapacity 主要关注使用到的最大、最小容量
-gcold
-gcoldcapacity
-gcpermcapacity
-compiler 输入JIT编译器编译过得方法、耗时信息
-printcompilation 输出已经被JIT编译的方法

jinfo

查看jvm进程配置信息(jdk6以上可以用java -XX:+PrintFlagsFinal查看默认参数)
使用java -XX:+PrintFlagsFinal查看默认参数,然后使用jinfo显示配置方式

java -XX:+PrintFlagsFinal
参数太多,可以自己一一寻找
C:\Users\q>jinfo -flag AdaptivePermSizeWeight 8732
-XX:AdaptivePermSizeWeight=20
如果是linux系统,可以通过awk写个简单脚本把所有的配置方式全都输出

jmap

jmap输出jvm进程的堆转储快照,或者可以在程序运行加上-XX:+HeapDumpOnOutOfMemoryError,在OOM异常时自动输出dump文件,以便进行分析,jmap还可以查询finalize执行队列、java堆和永久代的详细信息,如空间使用率、当前垃圾回收器是什么,但是有些功能只能在linux环境下使用

jmap  [option] vmid
-dump -dump:[live, ]format=b, file=filename,live表示只dump出存活的对象
-finalizerinfo 只有在linux平台下有效
-heap 显示堆详细信息,使用哪种回收器、参数配置、分代状况;linux下有效
-histo 显示类、实例数量、合计容量统计信息
-permstat 显示永久代内存状态;linux下有效
-F 当-dump虚拟机进程没有响应时,使用这个强制生成dump快照;linux下有效

jmap -dump:format=b,file=test.dump 3324
dumping heap to ......
Heap dump file created

jhat

与jmap搭配使用,分析jmap生成的快照;一般使用Elicpse Memory Analyzer或者VisualVM来进行快照分析,因为jhat分析dump比较耗时,而且不方便

jhat test.dump
..................
start http server on port 7000
server is ready

这样就可以通过http://localhost:7000/访问分析结果了,一般从Heap Histogram分析内存泄露

jstack

堆栈跟踪工具,用于分析线程停顿过长、线程死锁、死循环、请求外部资源耗时长等,通过堆栈信息就可以清楚知道线程停止在代码的哪一行

jstack [ option ] vmid
-F  正常输出没有响应则强制输出快照
-l 除堆栈外,显示锁的附加信息
-m  如果调用本地方法,显示C堆栈信息

C:\Users\q>jstack -l 13296
"EventAdmin Async Event Dispatcher Thread" daemon prio=6 tid=0x000000000c749000
nid=0xc40 in Object.wait() [0x00000000113ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000c42dc8d0> (a org.eclipse.osgi.framework.eventmgr
.EventManager$EventThread)
        at java.lang.Object.wait(Object.java:503)
        at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.getNextE
vent(EventManager.java:400)
        - locked <0x00000000c42dc8d0> (a org.eclipse.osgi.framework.eventmgr.Eve
ntManager$EventThread)
        at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(Even
tManager.java:336)

   Locked ownable synchronizers:
        - None

"[ThreadPool Manager] - Idle Thread" daemon prio=6 tid=0x000000000c746800 nid=0x
476c in Object.wait() [0x000000000dbff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000c3444c30> (a org.eclipse.equinox.internal.util.i
mpl.tpt.threadpool.Executor)
        at java.lang.Object.wait(Object.java:503)
        at org.eclipse.equinox.internal.util.impl.tpt.threadpool.Executor.run(Ex
ecutor.java:106)
        - locked <0x00000000c3444c30> (a org.eclipse.equinox.internal.util.impl.
tpt.threadpool.Executor)

   Locked ownable synchronizers:
        - None

"Java indexing" daemon prio=4 tid=0x000000000bb63000 nid=0x2a30 in Object.wait()
 [0x000000000ddff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000c300a090> (a org.eclipse.jdt.internal.core.searc
h.indexing.IndexManager)
        at java.lang.Object.wait(Object.java:503)
        at org.eclipse.jdt.internal.core.search.processing.JobManager.run(JobMan
ager.java:382)
        - locked <0x00000000c300a090> (a org.eclipse.jdt.internal.core.search.in
dexing.IndexManager)
        at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
        - None

"Bundle File Closer" daemon prio=6 tid=0x000000000c176000 nid=0x3f44 in Object.w
ait() [0x000000000dcff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000c324cf68> (a org.eclipse.osgi.framework.eventmgr
.EventManager$EventThread)
        at java.lang.Object.wait(Object.java:503)
        at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.getNextE
vent(EventManager.java:400)
        - locked <0x00000000c324cf68> (a org.eclipse.osgi.framework.eventmgr.Eve
ntManager$EventThread)
        at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(Even
tManager.java:336)

jdk5+,可以通过java.lang.Thread的getAllStackTraces()方法获取所有信息

hsdis

jsdis是一个反汇编插件,包含在虚拟机源码中,它可以容hotspot的-XX:+PrintAssembly指令调用它吧动态生成的本地代码还原为汇编代码输出,还伴随有价值的注释,可以根据CPU类型从Project kenai下载插件,放到JDK_HOME/jre/bin/server目录中即可,window需要自己通过源码编译

package com;

public class Bar {
    int a = 1;
    static int b = 2;
    public int sum(int c){
        return a + b + c;
    }
    public static void main(String[] args) {
        new Bar().sum(3);
    }
}

//由于我没有安装这个插件是报错的,有兴趣的可以试试
F:\worksoftware\eclipse\workspace\java\java8\src\com>java -XX:+PrintAssembly -Xc
omp -XX:CompileCommand=dontinline,*Bar.sum -XX:CompileCommand=compileonly,*Bar.s
um com.Bar
Improperly specified VM option 'PrintAssembly'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

jdk的可视化工具

jconsole和visualvm,这两个工具都在JAVA_HOME/bin目录下面,双击打开可用

jconsole

jconsole主要针对JMX MBean进行管理,我们其实可以自己通过java提供的MBean进行访问,包含概述、内存、线程、类、vm摘要、MBean6个栏目

  1. 概述整个vm进程运行数据纵览,堆内存情况、线程、类、cpu使用情况
  2. 内存相当于jstat的可视化命令
  3. 线程页相当于jstack的可视化命令,可自行模拟等待和死锁的代码

    visualVM

    visualvm提供了运行监视、故障处理 、性能分析(跟JProfiler、YourKit、profiling类似)能力,并且它基本不影响系统性能,可以直接用于生产环境(未做这个实验),dump堆、栈信息都非常方便
    visualvm

visualvm安装brace插件

选择工具→插件选择brace安装即可,然后选在左侧的进程进行trace

//测试程序
package com.omm;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class BTraceTest {
    public int add(int a, int b) {
        return a + b;
    }
    public void run(){
        int a = (int) (Math.random() * 1000);
        int b = (int) (Math.random() * 1000);
        System.out.println(add(a, b));
    }
    public static void main(String[] args) throws IOException {
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        BTraceTest bTraceTest=new BTraceTest();
        for (int i = 0; i < 10; i++) {
            bReader.readLine();
            bTraceTest.run();
        }
    }
}

运行后在brace标签页下面编写代码
/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class TracingScript {
    /* put your code here */
    @OnMethod(
        clazz="com.omm.BTraceTest",
        method="add",
        location=@Location(Kind.RETURN)
    )
    public static void func(@Self com.omm.BTraceTest instance ,int a,int b,@Return int result){
        println("调用堆栈");    
        jstack();
        println(strcat("方法参数A:",str(a)));
        println(strcat("方法参数B:",str(b)));
        println(strcat("方法结果:",str(result)));

    }
}

eclipse中console随便输入字符回车

btrace DEBUG: trackRetransforms is true
fd
186
fds
131

brace中ouput打印信息,可以清晰看到传递参数
** Compiling the BTrace script ...
*** Compiled
** Instrumenting 1 classes ...
*** Done
** BTrace up&running

*** Done
** BTrace up&running

调用堆栈
com.omm.BTraceTest.add(BTraceTest.java:7)
com.omm.BTraceTest.run(BTraceTest.java:12)
com.omm.BTraceTest.main(BTraceTest.java:19)
方法参数A:125
方法参数B:61
方法结果:186
调用堆栈
com.omm.BTraceTest.add(BTraceTest.java:7)
com.omm.BTraceTest.run(BTraceTest.java:12)
com.omm.BTraceTest.main(BTraceTest.java:19)
方法参数A:31
方法参数B:100
方法结果:131

brace可以打印调用堆栈,如上;官网BTrace on the web有进行性能监控、定位链接泄露、内存泄露、解决多线程竞争问题等案例。

总结

一般系统来说,现有的java虚拟机类装载、编译、内存管理、内存自动回收已经基本够用,在大的公司都有自定义的虚拟机,期望达到更高效;随着java用户量的增加,基于需求行业也出现了很多专业的jvm监控平台

一切性能优化的起始招式都是静静的观察系统运行状态,这也是专业的监控平台起来的原因;遇到问题不要着急,静心观察,症结自然无所遁形

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值