天天看点

性能监控与性能问题定位之:Java篇

转载 http://hi.baidu.com/higkoo/blog/item/f2583266e90e032aab184caf.html

阅读本文之前建立对Java(JVM、GC)和其Web服务程序有所了解。

    以下是Java自带的性能监控工具

jps(jps -mlvV) 与Linux上的ps类似,用来显示本地的java进程,可以查看本地运行着几个java程序,并显示他们的进程号。
jstat(man jstat | jstat -gc $jpid) 监视VM内存工具,可以用来监视VM内存内的各种堆和非堆的大小及其内存使用量。  
jmap(Memory Map |jmap -heap $jpid) 打印出某个java进程(使用pid)内存内的,所有‘对象’的情况(如:产生那些对象,及其数量)。
jstack(Linux特有| jstack -l $jpid) 观察jvm中当前所有线程的运行情况和线程当前状态。
jinfo(Linux特有| jinfo $jpid) 运行中的java程序的运行环境参数,参数包括Java System属性和JVM命令行参数
jconsole 结合上述功能的GUI工具,监控到:CPU、内存、线程数、类数量,展现VM摘要、Mbean集属性。
jvisualvm Java5之后内置的监控工具,和Jconsole相似,Dump功能强大!并有Profile功能。
jhat 用于解析Java的Dump文件,并启动Web服务供浏览。

   必要时我们需要抓取Java全内存的Dump来分析,抓取方式有如下:

jmap -dump <options>
jvm中添加参数:-XX:+HeapDumpOnOutOfMemoryError 在符合条件时会输出Dump。
hprof输出:java -agentlib:hprof[=options] ToBeProfiledClass | java -agentlib:hprof=help
jvisualvm:有界面,可生成各种镜像和Dump。

    那么Dump怎样分析呢?

jhat 用于解析Java的Dump文件,并启动Web服务供浏览。
mat(Memory Analyzer) 开源分析工具,可形成报告。官网:http://www.eclipse.org/mat/

    JDK还内置了一些演示程序供参考,给被监控程序添加启动参数后执行以下指令:

java -jar $JAVA_HOME/demo/management/MemoryMonitor/MemoryMonitor.jar 192.168.223.108:8950
java -jar $JAVA_HOME/demo/management/FullThreadDump/FullThreadDump.jar 192.168.223.108:8950
java -jar $JAVA_HOME/demo/management/JTop/JTop.jar 192.168.223.108:8950
java -jar $JAVA_HOME/demo/management/VerboseGC/VerboseGC.jar 192.168.223.108:8950

    OK,由于篇幅限制,接下来介绍一下商业监控工具吧:

Jprofiler http://www.ej-technologies.com/
JRockit http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html
Yourkit http://www.yourkit.com

    Jprofiler专门针对Java性能监控设计。

    JRockit则是一套JVM,有其配套的监控系统和工具。

    Yourkit没用过,出品了java和.net的监控。

废话不多说,下面我来演示一下如何Jprofile来定位Java的性能问题。以Jboss为例:

    在/usr/local/jboss/bin/run.conf里添加JAVA_OPTS:`-agentpath:libjprofilerti.so=port=8849`,启动Jboss。

    这里我做了示例程序,在此理解为某产品的三个版本。访问方式如下:

http://TestServer/Servlet/Demo?Version=1

http://TestServer/Servlet/Demo?Version=2

http://TestServer/Servlet/Demo?Version=3

    性能体现如下:

v1:正常,性能好且表现稳定(点击率曲线平稳在6600)

v2:性能下降明显(点击率曲线平稳在2800),需要尽快找出原因

v3:程序不稳定,运行越来越慢(刚启动是点击率约4200,随后渐跌至0),久了就宕机(后台抛内存溢出)

    使用Jprofile监控,问题轻松被定位。如下图:

        1、“CPU Views”部分为进行v2的测试过程截图。问题定位在加解密上,即这块需要优化。

        2、“Heap Walker”部分为进行v3的测试过程截图。问题定位在Session处理上,说明Session机制需要调整。

性能监控与性能问题定位之:Java篇

上述示例的代码也贴出来了,AES加密代码较多未贴需要读者自己实现了:

性能监控与性能问题定位之:Java篇

package com.higkoo.tester;

public class Demo extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)

        String version = request.getParameter("Version");

        response.setContentType("text/html ;charset=UTF-8");

        response.setHeader("Cache-Control", "no-cache");

        PrintWriter out = response.getWriter();

        String result = new String();

        String rand = UUID.randomUUID().toString();

         if(version==null) version = "0";

        switch(Integer.parseInt(version)){

            case 1:

                result = "Version1:" + ClacMD5(rand);

                break;

            case 2:

                result = "Version2:" + ClacAES(rand);

                break;

            case 3:               

                result = "Version3:" + MakeSession(request,rand);

                break;

            default:   

                result = "Default:" + "Hello higkoo !";

                break;

        }

        out.write(result);

        out.close();

    }

    public String ClacMD5(String str){

        MessageDigest md = MessageDigest.getInstance("MD5");

        byte[] array = md.digest(str.getBytes("utf-8"));

        StringBuffer sb =new StringBuffer();

        String result = new String();

        if(str !=null){

            for(int i =0; i < array.length; i++){

                    sb.append(Integer.toHexString((array[i]&0xFF)|0x100).toUpperCase().substring(1,3));

                }

            result = sb.toString();

        }

        return result;

    }

    public String ClacAES(String str){

        String kStr = "866563b712d204d82876d1153d06c1f11";

        String result = new String();

        String eStr = AESUtil.encrypt(AESUtil.encrypt(AESUtil.encrypt(str.replaceAll("-", ""), kStr),kStr),kStr);

        result = AESUtil.decrypt(AESUtil.decrypt(AESUtil.decrypt(eStr, kStr),kStr),kStr);       

        return result.toUpperCase();

    }

    public String MakeSession(HttpServletRequest request,String str) {

        HttpSession session=request.getSession(true);

        session.setAttribute("accessCount", str);

        return session.getId() ;

    }

}