Btrace详细指南(JDK7,监控HashMap扩容)

背景

    JAVA中如何排查疑难杂症,如何动态获取应用信息,我们有BTrace!

    PS:集团有大杀器arthas,这里我们先从最原始最广泛的BTrace开始,后面可以玩玩Greys(开源,强于BTrace)。

    应用被tracle后,相关class会被改变,恢复需要重新编译,请自行把握。

一、载安装BTtrace

wiki:https://github.com/jbachorik/btrace/wiki

BTrace博客:http://jbachorik.github.io/btrace/

JIRA(深入必看):https://kenai.com/jira/browse/BTRACE/?selectedTab=com.atlassian.jira.jira-projects-plugin:summary-panel

官网:https://kenai.com/projects/btrace/

github(1.3):https://github.com/jbachorik/btrace

IDE环境:引入BTrace相关Jar,在eclipse/IDEA编写;使用jvisualvm编写。

代码自动生成工具(使用必备):https://btrace.org/btrace/

下载最新版(1.3,JDK7以上):https://github.com/jbachorik/btrace/releases/tag/v1.3

BTrace Maven插件:https://github.com/btraceio/btrace-maven

BTrace1.2开发者说明:https://kenai.com/projects/btrace/pages/DeveloperGuide

BTrace1.2 API DOC:https://btrace.kenai.com/javadoc/1.2/index.html

配置环境变量:

BTRACE_HOME:~/user/btrace

PATH:$BTRACE_HOME/bin

JAVA_VERSION:MAC特有,详见btrace脚本内容,后文有说明。

Maven仓库(只有1.2.X版本,更高版本请自行编译发布到私服):



 <!-- 1.2版本-->

<dependency>

    <groupId>com.sun.tools.btrace</groupId>

    <artifactId>btrace-agent</artifactId>

    <version>1.2.3</version>

</dependency>

<dependency>

    <groupId>com.sun.tools.btrace</groupId>

    <artifactId>btrace-boot</artifactId>

    <version>1.2.3</version>

</dependency>



注意:应用程序端不需要引入BTrace相关Jar,但为了编写脚本方便(IDE提示),可以以Provided引入它,不必Complie。



二、BTtrace常用脚本

解压btrace发现在%BTRACE_HOME%\bin有三个脚本:btrace、btracec、btracer,我们通过这三个脚本使用BTrace。

中文简述:

btrace:对运行中的JAVA程序执行btrace脚本。

btracec:编译btrace脚本。

btracer:让BTrace随JAVA应用启动加载。

btracer注意事项:

原理:java -javaagent:btrace-agent.jar=[<agent-arg>[,<agent-arg>]*]? <launch-args>

场景:When no custom agent arguments are required the btracer utility script may be used instead.

完整用法:https://github.com/jbachorik/btrace/wiki/btracer

英文完整说明:

<btrace>/bin/btrace <PID> <trace_script> will attach to the java application with the given PID and compile and submit the trace script

<btrace>/bin/btracec <trace_script> will compile the provided trace script

<btrace>/bin/btracer <compiled_script> <args to launch a java app> will start the specified java application with the btrace agent running and the script previously compiled by btracec loaded

详细看看btrace(1.3.4)脚本内容:



#! /bin/sh

if [ -z "$BTRACE_HOME" -o ! -d "$BTRACE_HOME" ] ; then

  # resolve links - $0 could be a link to btrace's home

  PRG="$0"

  progname=`basename "$0"`

  BTRACE_HOME=`dirname "$PRG"`/..

  BTRACE_HOME=`cd "$BTRACE_HOME" && pwd`

fi

if [ -f "${BTRACE_HOME}/build/btrace-client.jar" ] ; then

    if [ "${JAVA_HOME}" != "" ]; then

         TOOLS_JAR="${JAVA_HOME}/lib/tools.jar"

         if [ ! -f ${TOOLS_JAR} ] ; then

           case "`uname`" in

             Darwin*)

               # In older JDK versions for Mac OS X, tools.jar is classes.jar

               # and is kept in a different location. Check if we can locate

               # classes.jar based on ${JAVA_VERSION}

               TOOLS_JAR="/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Classes/classes.jar"

               # if we can't find, try relative path from ${JAVA_HOME}. Usually,

               # /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home

               # is JAVA_HOME. (or whatever version beyond 1.6.0!)

               if [ ! -f ${TOOLS_JAR} ] ; then

                 TOOLS_JAR="${JAVA_HOME} /../Classes/classes.jar"

               fi

               # If we still can't find, tell the user to set JAVA_VERSION.

               # This way, we can avoid zip file errors from the agent side

               # and "connection refused" message from client.

               if [ ! -f ${TOOLS_JAR} ] ; then

                 echo "Please set JAVA_VERSION to the target java version"

                 exit 1

               fi

               ;;

           esac

         fi

       ${JAVA_HOME}/bin/java -cp ${BTRACE_HOME}/build/btrace-client.jar:${TOOLS_JAR}:/usr/share/lib/java/dtrace.jar com.sun.btrace.client.Main $*

    else

       echo "Please set JAVA_HOME before running this script"

       exit 1

    fi

else

    echo "Please set BTRACE_HOME before running this script"

    exit 1

fi



MAC 版本如果遇到错误,请留意%JAVA_VERSION%这个环境变量,原因见上面btrace脚本源码。

【${JAVA_HOME}/bin/java -cp ${BTRACE_HOME}/build/btrace-client.jar:${TOOLS_JAR}:/usr/share/lib/java/dtrace.jar com.sun.btrace.client.Main $*】btrace启动核心靠它!。

三、使用示例(监控HashMap扩容时的上下文情况)

简要说明:

table:哈希表数组;newCapacity:哈希表空间容量,即table数组长度;size:哈希表有效数据长度;threshold:有效数据个数阀值;


    void addEntry(int hash, K key, V value, int bucketIndex) {

        if ((size >= threshold) && (null != table[bucketIndex])) {    //达到阀值 && 当前key对应的bucket被占用,触发扩容

            resize(2 * table.length);

            hash = (null != key) ? hash(key) : 0;

            bucketIndex = indexFor(hash, table.length);

        }

        createEntry(hash, key, value, bucketIndex);

    }


原始代码:


public class ExpandCapacity {

    public static final int ONE_MB = 1024 * 1024;

    public static void main(String[] args) throws Exception {

        mapExpandCapacity();

    }

    /**

     * java.util.HashMap#resize(int)

     * 何时扩容,发生扩容时候的上下文情况

     */

    public static void mapExpandCapacity() throws Exception {

        new Thread(new Runnable() {

            public void run() {

                while (true) {

                    //这是模拟主要逻辑

                    Map<Integer, Byte[]> map = new HashMap<Integer, Byte[]>();//16,0.75 16*0.75=12

                    int size = 100;

                    for (int i = 0; i < size; i++) {

                        map.put(i, new Byte[ONE_MB]);

                    }

                    System.out.println("Expand SIZE = " + map.size());

                    try {

                        TimeUnit.SECONDS.sleep(10);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

            }

        }, "mapExpandCapacity").start();

        TimeUnit.HOURS.sleep(1);

    }

}


trace脚本,特别注意OnMethod的中@Location#Where默认值是Before,代表方法被调用,但还未执行其内容:


@BTrace

public class ExpandCapacityBtrace {

    @OnMethod(clazz = "java.util.HashMap", method = "resize",

            location = @Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/"))

    public static void traceMapExpandCapacity(@ProbeClassName String probeClass, @ProbeMethodName String probeMethod,

                                              @Self Object self,int newCapacity) {

        String point = Strings.strcat(Strings.strcat(probeClass, "."), probeMethod);//java/util/HashMap.resize

        Class clazz = classForName("java.util.HashMap");

        println(Strings.strcat(point, "======"));

        //获取实例protected变量

        Map.Entry[] table= (Map.Entry[]) get(field(clazz, "table", true), self);

        int threshold = getInt(field(clazz, "threshold", true), self);

        int size = getInt(field(clazz, "size", true), self);

        println(Strings.strcat("newCapacity:", str(newCapacity)));

        println(Strings.strcat("table.length:", str(table.length)));

        println(Strings.strcat("size:", str(size)));

        println(Strings.strcat("threshold:", str(threshold)));

        println(Strings.strcat(point, "------------"));

    }

}


输出(摘录核心,并进行格式化):


java/util/HashMap.resize======

newCapacity:32

table.length:16

size:16

threshold:12

java/util/HashMap.resize------------

java/util/HashMap.resizee======

newCapacity:64

table.length:32

size:32

threshold:24

java/util/HashMap.resize------------

java/util/HashMap.resizee======

newCapacity:128

table.length:64

size:64

threshold:48

java/util/HashMap.resize------------


初始(New HashMap()):newCapacity=16,size=0,table.length=16,threshold=12;

添加100个有效数据,会发生多次扩容,调用resize,首次调用:newCapacity=32,size=16,entry.length:16,threshold=12;

threshold每次执行完resize会改变为(int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1)。

四、常见问题

Q:btrace默认将信息打印到控制台,如何将信息打印到文件?

btrace pid btrace_scrpit.java > console.log

>代表覆盖,>>代表追加,其实就是把控制台的东东重定向到文件。

Q:典型错误NullPointerException:



......

Exception in thread "main" java.lang.NullPointerException

        at com.sun.btrace.client.Client.submit(Client.java:361)

        at com.sun.btrace.client.Main.main(Main.java:189)

DEBUG: sending exit command

Exception in thread "Thread-0" java.lang.IllegalStateException

        at com.sun.btrace.client.Client.send(Client.java:466)

        at com.sun.btrace.client.Client.sendExit(Client.java:400)

        at com.sun.btrace.client.Main$2.run(Main.java:229)

        at java.lang.Thread.run(Thread.java:745)



原因一:

应用程序端使用BTrace版本和客户端不一致。

    为了方便BTrace脚本编写,IDE引入BTrace Jar,设置了Compile范围,应用启动加载时会加载相关class。

    而运行btrace命令时,其BTrace版本和应用端的BTrace版本不一致,产生异常。

这里直接将IDE引入的BTrace设置成Provided。

原因二:

    主要由于Btrace默认使用%JAVA_HOME%/lib/tools.jar,而应用程序类加载路径(-cp/-classpath)没有tools.jar,因此我们需要把tools.jar加到类路径下。

    相关git issue:

https://github.com/jbachorik/btrace/issues/182

https://github.com/jbachorik/btrace/issues/169(解决方案)

https://github.com/jbachorik/btrace/issues/168

https://github.com/jbachorik/btrace/issues/182(解决方案)


五、BTrace详解

BTrace 的使用通过Annotations,详情:https://github.com/jbachorik/btrace/wiki/BTrace-Annotations

中文翻译(个人水平有限,建议中英对照):

  • 方法注解说明 

@OnMethod:指定使用当前注解的方法应该在什么情况下触发,规范[@OnMethod(clazz=<cname_spec>[, method=<mname_spec>]? [, type=<signature>]? [, location=<location_spec>]?)]

    claszz属性,指定要匹配的类的全限定类名,可以用正则表达式:"/类名的Pattern/"匹配,用”+类名”匹配所有子类,用”@某某注解”匹配用该注解注解过的类。

    method属性,指定要匹配的方法名称,可以用正则表达式:/方法名称的Pattern/匹配。

    type属性,类似JAVA方法声明,但不包括方法名,参数名,异常。如void(java.lang.String)可以用于匹配:public void funcName(String param) throws Exception。

    location属性,可简单认为指明该BTrace方法在何时执行(如THROW/RETURN/ENTRY,详情参见@Location) 。

@OnTimer:指定一个定时任务。

@OnExit:当脚本运行Sys.exit(code)时触发。

@OnError:当脚本运行抛出异常时触发。

@OnEvent:脚本运行时Ctrl+C可以发送事件。

@OnLowMemory:让你指定一个阀值,内存低于阀值触发。

@Location注意点:

Kind.ENTRY意指进入匹配probe点,与@Location设置的clazz和method没有任何关系。

Kind.CALL意指从某个匹配probe的方法中调用了匹配A class method的点,一定要和clazz,method配合使用。clazz和method的默认值为"",所以不能被匹配。

 Kind.ENTRY只关注调用X方法(OnMethod的clazz,method),Kind.CALL还关注X方法中,还调用了哪些类哪些方法(Location的class,method)。

  • 方法参数注解说明 

@Self:用来指定被trace方法的this,可参考例子AWTEventTracer.java 和 AllCalls1.java。

@ProbeClassName:目标类名。

@ProbeMethodName:目标方法名。

@TargetInstance:配合@Location#Kind.CALL使用,匹配@Location#clazz的Class实例。

@TargetMethodOrField:配合@Location#Kind.CALL使用,匹配@Location#method的MethodOrField值。

@Retrun:配合@Location#Kind.Return使用,用来指定被trace方法的返回对象,可参考例子Classload.java。

@Duration:配合@Location#Kind.Return/ Kind.ERROR使用,目标方法执行时间,单位是纳秒。

如何获取方法参数?可参见@Kind#CALL说明。

如方法java.util.HashMap#resize(int newCapacity),要获取newCapacity,则可以在trace方法当参数注入,如下:


public static void traceMapExpandCapacity(@ProbeClassName String probeClass, @ProbeMethodName String probeMethod, @Self Object self,int newCapacity)


其中newCapacity则是resize调用时的newCapacity参数。

 @TargetInstance 和 @TargetMethodOrField的理解

如果是probe A class方法调用匹配了B class,@TargetInstance 返回的就是B class,@Self返回的就是A class。

@Retrun:用来指定被trace方法的返回对象,可参考例子Classload.java。

Trace Scripts 说明,主要是一些约束,描述了能做什么,不能做什么:https://github.com/jbachorik/btrace/wiki/Trace-Scripts

samples目录有很多样例,想写又不会写时,可以进去看看,这有中文说明:http://mgoann.iteye.com/blog/1409667

六、参考文献

1. btrace记忆:http://agapple.iteye.com/blog/962119

2. btrace一些你不知道的事(源码入手),这篇很有料,请看:http://agapple.iteye.com/blog/1005918

附:思维导图(转载)

时间: 2024-08-02 21:12:22

Btrace详细指南(JDK7,监控HashMap扩容)的相关文章

Windows、macOS和Linux平台设置HTTPS以及自签名证书详细指南

本文讲的是Windows.macOS和Linux平台设置HTTPS以及自签名证书详细指南, 在本文中,我会向大家介绍如何在Windows.macOS和Ubuntu Linux上设置使用HTTPS与ASP.NET Core的本地开发环境,另外,我还将介绍如何创建自签名证书并将其添加到受信任的根证书存储区,以消除烦人的浏览器提示消息.最后,我将介绍如何设置用于ASP.NET Core的内置Web服务器的Kestrel来使用HTTPS. 创建自签名证书,设置信任项,就可以避免烦人的浏览器提示,记录了自

对Linux进行详细的性能监控的方法

  这是我们正在进行的Linux命令和性能监控系列的一部分.vmstat和iostat两个命令都适用于所有主要的类unix系统(Linux/unix/FreeBSD/Solaris). 如果vmstat和iostat命令在你的系统中不可用,请安装sysstat软件包.vmstat,sar和iostat命令都包含在sysstat(系统监控工具)软件包中.iostat命令生成CPU和所有设备的统计信息.你可以从这个连接中下载源代码包编译安装sysstat,但是我们建议通过YUM命令进行安装. 在Li

《阿里巴巴Java开发规约》插件使用详细指南

阿里巴巴于10月14日在杭州云栖大会上,正式发布众所期待的<阿里巴巴Java开发规约>扫描插件.今天,阿里妹为大家详细介绍一下IDEA插件与Eclipse插件的安装使用. 插件下载地址 https://github.com/alibaba/p3c 或者在Github直接搜索p3c 阿里巴巴Java开发手册IDEA插件 使用指南 通过Jetbrains官方仓库安装 1.打开 Settings >> Plugins >> Browse repositories... 2.在

[原创]windows2003手工安装配置php5详细指南

php5|window|windows2003|原创 今天,服务器进行PHP环境的配置,先在百度搜集了一些相关资料进行参考,然后开始手工配置PHP5环境(个人比较喜欢绿色免安装的东西). 在Windows环境下安装PHP有两种方法:手工配置环境或者使用PHP安装包进行安装.据PHP官方手册上称安装PHP最好的选择是手工安装.在手工安装中安装PHP最好的方式便是将所有PHP有关的文件都放入同一目录,并在系统的PATH环境变量中设置此目录. 在WINDOWS环境配置PHP5,你必须明白这两个名词的函

Android RoboGuice使用详细指南

Android RoboGuice使用指南(1) 概述 Android RoboGuice使用指南(2) 第一个例子Hello World Android RoboGuice使用指南(3) Bindings 概述 Android RoboGuice使用指南(4) Linked Bindings Android RoboGuice使用指南(5) Binding Annotations Android RoboGuice使用指南(6) Instance Bindings Android RoboGu

Bookmarklet编写详细指南

前一段日子,我写了两个Bookmarklet----"短网址生成"和"短网址还原". 它们用起来很方便,除了我本人之外,其他朋友也在用.第一次发布Bookmarklet,就能有用户,我挺满意的. 下面就是我整理的<Bookmarklet编写指南>,供自己和需要的朋友参考. 一.什么是Bookmarklet? Bookmarklet是一个复合词,由Bookmark(书签)和-let(小的)构成,中文可以译成"书签工具". 它在形式上与&

vc.net2003下安装和使用blitz++详细指南

一.起因 安装和使用 blitz++ 时经常遇到问 , 查看了 ccboy 写的< VC++ _NET 2003 –安装和使用 Boost 1_30 和 Blitz++0_6 >,但他写的不详细 , Blitz 的官方文档也不清楚 , 只好自己来 , 花了不少时间才初步弄清楚.   二. Blitz 简介 大家知道 Fortran 是公认的科学计算编程语言 , 大量数值计算库都是基于 fortran 的 , 如 lapack. 而 blitz++ 则为科学计算人员提供了面向对象的 C++ 的数

Android App支付系列(一):微信支付接入详细指南(附官方支付demo)_Android

写在前面 一家移动互联网公司,说到底,要盈利总是需要付费用户的,自己开发支付系统显然是不明智的,国内已经有多家成熟的移动支付提供商,腾讯就是其中之一.梳理了下微信支付的接入,今天给大家分享下腾讯旗下的微信支付SDK的接入流程. 接入流程 1.申请开发者资质 地址:https://open.weixin.qq.com/ 使用公司管理者/高层帐号登录微信开放平台,进入"账号中心",进行开发者资质认证,需要填写公司资料,包括但不限于,公司注册号,公司营业执照,公司对外办公电话,公司对公银行卡

Android App支付系列(二):支付宝SDK接入详细指南(附官方支付demo)

一家移动互联网公司,说到底,要盈利总是需要付费用户的,自己开发支付系统对于资源有限的公司来说显然不太明智,国内已经有多家成熟的移动支付提供商,阿里就是其中之一. 笔者在此总结了下阿里旗下支付宝Android SDK支付的接入流程,供后来者参考. 接入流程如下: 1 签约成为支付宝商户 签约地址:https://b.alipay.com/, 只有成为签约商户的开发者才能具备集成支付宝app支付的资格. 签约资料:1)营业执照 2)APP说明文档 3)商户经营信息.商户联系人等信息 必要时还需提供A