Java-debug-tool

Java-debug-tool

微信公眾號(hào):一字馬胡

Java-debug-tool解決什么問(wèn)題

java-debug-tool

Java-debug-tool是為了解決日常問(wèn)題排查的痛點(diǎn)而設(shè)計(jì)的钓简,問(wèn)題排查分成兩個(gè)主要階段,問(wèn)題定位和問(wèn)題修復(fù)汹想,問(wèn)題定位是說(shuō)找到問(wèn)題的原因外邓,問(wèn)題修復(fù)是說(shuō)將問(wèn)題解決,使得系統(tǒng)恢復(fù)正常運(yùn)行古掏。
對(duì)于問(wèn)題定位來(lái)說(shuō)损话,我們的需求是:

  • 能知道方法入?yún)⒑头祷刂担蛘邟伋龅漠惓P畔?/li>
  • 當(dāng)方法有多個(gè)出口的時(shí)候槽唾,可以知道方法是從什么地方退出的丧枪,或者是從什么地方拋出異常的;
  • 一次方法調(diào)用的執(zhí)行路徑是怎么樣的庞萍,每一行代碼的耗時(shí)又是多少拧烦;
  • 獲取到方法執(zhí)行過(guò)程中的局部變量信息;

本質(zhì)上钝计,問(wèn)題定位的需求是實(shí)現(xiàn)單步調(diào)試恋博,因?yàn)檫@樣是最容易發(fā)現(xiàn)問(wèn)題出在什么地方的,但是對(duì)于java來(lái)說(shuō)私恬,單步調(diào)試技術(shù)會(huì)停頓整個(gè)JVM债沮,所以只能在測(cè)試的時(shí)候使用這種技術(shù),對(duì)于生產(chǎn)環(huán)境來(lái)說(shuō)就不能使用了本鸣,所以對(duì)于線上問(wèn)題排查來(lái)說(shuō)疫衩,基本可以不用考慮單步調(diào)試,但是如果集群有流量摘除等功能的話荣德,倒是可以使用闷煤;java-debug-tool解決了這個(gè)問(wèn)題,可以模擬單步調(diào)試的同時(shí)不會(huì)停頓正在運(yùn)命爬,使用行的JVM曹傀,下面會(huì)介紹Java-debug-tool到底實(shí)現(xiàn)了一些什么功能辐脖。

找到了問(wèn)題出現(xiàn)的原因饲宛,接著就是問(wèn)題修復(fù)值纱,問(wèn)題修復(fù)最大的痛點(diǎn)其實(shí)是恢復(fù)生產(chǎn)薄湿,對(duì)于java來(lái)說(shuō),恢復(fù)生產(chǎn)意味著需要重啟JVM讯榕,這樣就會(huì)造成問(wèn)題修復(fù)時(shí)間變長(zhǎng)久锥,Java-debug-tool為此提供了技術(shù)支持Java Instrumentation技術(shù)家淤,可以在運(yùn)行時(shí)的JVM中替換類的字節(jié)碼,實(shí)現(xiàn)熱修復(fù)瑟由。


Java-debug-tool不能解決什么問(wèn)題

  • (1)如果需要做性能優(yōu)化分析絮重,Java-debug-tool可能支持的力度很小,雖然可以通過(guò)Java-debug-tool觀察到每一行代碼的執(zhí)行耗時(shí),但是也僅僅是觀察青伤,所以性能問(wèn)題還是需要其他專業(yè)的工具來(lái)進(jìn)行督怜;
  • (2)非JVM自身問(wèn)題,比如機(jī)器CPU狠角、磁盤I/O等問(wèn)題号杠,Java-debug-tool就無(wú)能為力了,Java-debug-tool專注于解決JVM自身的問(wèn)題丰歌;
  • (3)Java-debug-tool僅支持方法級(jí)別的觀察姨蟋,無(wú)法觀察到整體的調(diào)用鏈路,后續(xù)可能會(huì)支持多級(jí)方法鏈路的觀察立帖,但可能性不大眼溶,因?yàn)橐С诌@種方法間調(diào)用鏈路追蹤,就得增強(qiáng)多個(gè)方法晓勇,而增強(qiáng)方法是對(duì)運(yùn)行時(shí)有一定損耗的偷仿,如果一個(gè)方法調(diào)用鏈路特別長(zhǎng)(對(duì)于java來(lái)說(shuō)一般調(diào)用鏈路都很長(zhǎng)),那么就悲劇了宵蕉;
  • (4)Java-debug-tool不支持遞歸方法的觀察酝静,這個(gè)功能實(shí)現(xiàn)起來(lái)也是非常麻煩,而且極其不可控羡玛,所以千萬(wàn)不要用Java-debug-tool去觀察一個(gè)遞歸方法别智,切記;

如何使用

使用

首先需要下載安裝腳本:

wget https://github.com/pandening/storm-ml/releases/download/6.0/javadebug-tool-install.sh

之后執(zhí)行:

sh javadebug-tool-install.sh 

如果看到屏幕輸出:

welcome to use java-debug-tool

就說(shuō)明安裝成功了稼稿,可以使用工具了薄榛!

開(kāi)發(fā)

Java-debug-tool使用Java開(kāi)發(fā),下面介紹如何使用Java-debug-tool進(jìn)行問(wèn)題排查让歼;

  • (1)下載Java-debug-tool代碼敞恋;
  • (2)進(jìn)入script目錄,執(zhí)行javadebug-pack.sh腳本執(zhí)行編譯打包谋右,要求JDK 1.8 +硬猫,并且一定要執(zhí)行javadebug-pack.sh腳本之后再使用;
  • (3)如果是Spring項(xiàng)目改执,則只需要將下面的bean配置到項(xiàng)目中即可實(shí)現(xiàn)JVM啟動(dòng)之后Java-debug-tool Agent自動(dòng)attach到目標(biāo)JVM上的功能啸蜜,如果不是Spring項(xiàng)目,請(qǐng)看(4)
    <!-- dynamic debug bean -->
    <bean id = "javaDebugInitializer" class="io.javadebug.spring.JavaDebugInitializer" factory-method="initializer" destroy-method="destroy" lazy-init="false"/>
  • (4)Java-debug-tool不要求在目標(biāo)JVM啟動(dòng)的時(shí)候就必須attach到目標(biāo)JVM上辈挂,可以動(dòng)態(tài)attach衬横,在目錄 /bin下有多個(gè)可用的腳本,方便用于動(dòng)態(tài)attach到目標(biāo)JVM上终蒂,無(wú)論如何蜂林,你都需要首先知道目標(biāo)JVM的進(jìn)程id遥诉,然后執(zhí)行一個(gè)腳本就可以動(dòng)態(tài)attach到目標(biāo)JVM上:
./javadebug-agent-launch.sh PID

這樣就可以在目標(biāo)JVM上啟動(dòng)一個(gè)tcp服務(wù),默認(rèn)地址為:127.0.0.1:11234噪叙,如果你想要指定其他的地址突那,可以使用下面的命令:

./javadebug-agent-launch.sh PID@IP:PORT

之后就可以在IP:PORT啟動(dòng)tcpServer,attach到目標(biāo)JVM上之后构眯,就可以連接目標(biāo)JVM進(jìn)行動(dòng)態(tài)調(diào)試了愕难,連接到目標(biāo)JVM只需要執(zhí)行下面的命令即可:

 ./javadebug-client-launch.sh

默認(rèn)就是連接 127.0.0.1:11234,如果attach目標(biāo)JVM的時(shí)候指定的地址不是這個(gè)惫霸,需要顯示指定地址:

 ./javadebug-client-launch.sh IP:PORT


命令詳解

Java-debug-tool目前支持的命令不多猫缭,下面分別介紹一下當(dāng)前支持的核心命令。首先介紹一下命令輸出界面信息介紹:

---------------------------------------------------------------------------------------------
命令              :mt
命令執(zhí)行Round       :1
客戶端ID           :10000
客戶端類型           :client:1
協(xié)議版本            :version:1
命令耗時(shí)            :179 (ms)
STW時(shí)間           :45 (ms)
---------------------------------------------------------------------------------------------
[ReturnTest.getIntVal] with params
[1]
[0 ms] (37)
[0 ms] (43) [startTime = 1559358148073]
[0 ms] (44) [strTag = the return/throw line test tag]
[0 ms] (45)
[0 ms] (47)
[0 ms] (51)
[3 ms] (52) [paramModel = 1.1]
[0 ms] (53)
return value:[101]  at line:53 with cost:5 ms

---------------------------------------------------------------------------------------------

每個(gè)輸出字段都介紹一下:

字段 含義
命令 本次輸出執(zhí)行的命令是什么 就是你輸入的命令名稱
命令執(zhí)行Round 這個(gè)調(diào)試客戶端和目標(biāo)JVM交互了幾次 交互次數(shù)
客戶端ID 每個(gè)客戶端首次連接服務(wù)端都會(huì)被分配一個(gè)ContextId壹店,后續(xù)的交互都需要將這個(gè)ID帶上 唯一ID
客戶端類型 這是一個(gè)保留字段猜丹,Java-debug-tool認(rèn)為第一個(gè)連接到目標(biāo)JVM的調(diào)試客戶端應(yīng)該是一個(gè)Master Client,權(quán)限最高
協(xié)議版本 防偽硅卢,只有是從服務(wù)端拿到的協(xié)議才能繼續(xù)交互
命令耗時(shí) 命令的執(zhí)行耗時(shí)射窒,從命令輸入處理開(kāi)始計(jì)算,到命令結(jié)果展示出來(lái)結(jié)束将塑,所以是客戶端耗時(shí) + 服務(wù)端耗時(shí)
STW時(shí)間 動(dòng)態(tài)增強(qiáng)字節(jié)碼涉及到JVM字節(jié)碼替換脉顿,會(huì)造成STW,這個(gè)時(shí)間就記錄到底STW了多長(zhǎng)時(shí)間点寥,這個(gè)時(shí)間會(huì)比實(shí)際STW的時(shí)間長(zhǎng)艾疟,只是一個(gè)粗略的時(shí)間 如果一個(gè)方法被一個(gè)client增強(qiáng)過(guò)了,后續(xù)的client就不能增強(qiáng)了敢辩,除非增強(qiáng)該方法的client退出蔽莱,其他client才能繼續(xù)增強(qiáng);同時(shí)戚长,一個(gè)client增強(qiáng)過(guò)的方法盗冷,其他client可以共享

接著就是具體方法的執(zhí)行路徑信息,比如上面這個(gè)例子同廉,說(shuō)明本次觀察的方法執(zhí)行是 "ReturnTest.getIntVal"仪糖,方法入?yún)⑹?,方法執(zhí)行路徑是37-43-44-45-47-51-52-53恤溶,最終從53行退出乓诽,其中第52行耗時(shí)3ms帜羊,其他行耗時(shí)小于1ms咒程,所以無(wú)法收集到,最終方法的執(zhí)行結(jié)果是101讼育,本次方法耗時(shí)5ms帐姻,并且可以看到43稠集、44、52行都有變量賦值信息饥瓷,格式為 varName = varVal.toString()剥纷,需要注意的是,varName可能是錯(cuò)誤的呢铆,但是varVal是正確的晦鞋,如果有多個(gè),按照賦值順序展示棺克;這是方法正常返回的結(jié)果展示悠垛,下面看一個(gè)方法拋出異常的結(jié)果展示:

---------------------------------------------------------------------------------------------
命令              :mt
命令執(zhí)行Round       :1
客戶端ID           :10001
客戶端類型           :client:0
協(xié)議版本            :version:1
命令耗時(shí)            :75 (ms)
STW時(shí)間           :0 (ms)
---------------------------------------------------------------------------------------------
[ReturnTest.getIntVal] with params
[7]
[0 ms] (37)
[0 ms] (43) [startTime = 1559358921527]
[0 ms] (44) [strTag = the return/throw line test tag]
[0 ms] (45)
[0 ms] (47)
[0 ms] (51)
[0 ms] (54)
[0 ms] (59)
[0 ms] (73) [paramModel = ParamModel{intVal=0, doubleVal='0.0'}]
[0 ms] (74)
[0 ms] (75)
[0 ms] (76) [subVal = 200]
[0 ms] (78)
[0 ms] (82)
throw exception:[java.lang.IllegalStateException: error occ with in:7]  at line:82 with cost:0 ms

---------------------------------------------------------------------------------------------

可以看到本次方法執(zhí)行路徑,參數(shù)為7娜谊,在82行拋出了異常确买,其他信息和正常返回時(shí)類似,就不做過(guò)多解釋了纱皆。

methodTrace命令

就像命令名稱一樣湾趾,這個(gè)命令是用于觀察方法執(zhí)行路徑的,可以使用mt來(lái)替代命令派草,該命令參數(shù)較多搀缠,但是大部分都是可選的,下面先介紹每一個(gè)參數(shù)的含義近迁,然后再介紹如何實(shí)現(xiàn)具體的功能胡嘿。

命令基本格式:
mt -c <class> -m <method>

可選參數(shù):

  • -d :如果目標(biāo)類中的目標(biāo)方法是重載方法,那么你需要提供這個(gè)參數(shù)钳踊,比如int a(int a) => desc = "(I)I"衷敌;

  • -t:選擇具體的功能類型,可選項(xiàng)為:

    • return:當(dāng)方法正常退出的時(shí)候拓瞪,獲取到一次方法鏈路信息缴罗;
    • throw:方方法拋出異常的時(shí)候,獲取到一次方法鏈路信息祭埂;
    • record:記錄方法調(diào)用信息面氓,用于回放流量;
    • custom:用于實(shí)現(xiàn)用戶自己輸入?yún)?shù)觀察蛆橡,或者回放record的流量進(jìn)行觀察舌界,當(dāng)然,如果只是想發(fā)生一次請(qǐng)求也是可以的泰演;
    • watch:等待特定的參數(shù)呻拌,使用Spring表達(dá)式進(jìn)行參數(shù)匹配,當(dāng)匹配到目標(biāo)參數(shù)之后睦焕,會(huì)返回方法鏈路信息藐握,如果Spring表達(dá)式有誤靴拱,那么會(huì)直接在第一次方法調(diào)用之后返回;
  • -i:用于接收用戶的參數(shù)輸入猾普,比如當(dāng)t=custom的時(shí)候袜炕,i參數(shù)就是用戶指定的參數(shù),這個(gè)參數(shù)是通過(guò)特殊處理的json字符串初家,java-debug-tool將提供工具接口來(lái)生成這個(gè)字符串偎窘,當(dāng)t=watch的時(shí)候,i參數(shù)就是用于匹配參數(shù)的Spring表達(dá)式溜在。

  • -n:當(dāng)t=record的時(shí)候评架,n參數(shù)的含義就是需要錄制的流量數(shù)量,當(dāng)前僅允許錄制10個(gè)以內(nèi)炕泳;

  • -time:當(dāng)t=record的時(shí)候纵诞,該參數(shù)的含義是錄制的時(shí)間限制,超出則停止錄制培遵;

  • -u:當(dāng)t=custom的時(shí)候浙芙,如果提供了u參數(shù),那么i參數(shù)將被忽略籽腕,u代表record的流量下標(biāo)嗡呼,從0開(kāi)始,如果u參數(shù)獲取到了具體的流量皇耗,那么本次custom輸入的參數(shù)就會(huì)從u參數(shù)取出來(lái)的流量中拿到參數(shù)南窗,如果t=record,并且u參數(shù)合法郎楼,那么就不會(huì)進(jìn)行錄制万伤,而是會(huì)從錄制好的流量中取出代表u下標(biāo)的流量,用戶可以查看具體的流量信息(包括該流量的方法鏈路)呜袁;

  • -e:如果t=throw敌买,那么如果-e內(nèi)容合法,那么該參數(shù)就代表需要等待的目標(biāo)異常阶界,如果參數(shù)不合法虹钮,只要遇到一個(gè)異常,本次觀察就會(huì)結(jié)束膘融;當(dāng)t=custom的時(shí)候芙粱,該參數(shù)用于匹配自定義輸入,也就是說(shuō)氧映,如果你希望觀察自定義輸入的執(zhí)行路徑春畔,你需要在custom類型下指定-e參數(shù),內(nèi)容是用于匹配輸入的Spring表達(dá)式;

  • -s:有些情況下拐迁,你可能只需要看方法調(diào)用的路徑蹭劈,不需要耗時(shí)信息疗绣,或者不需要變量信息线召,那么這個(gè)參數(shù)有很有用,因?yàn)榭赡苡行┳兞亢荛L(zhǎng)多矮,展示出來(lái)很難看,而有些時(shí)候你只需要看看方法到底是從哪里退出來(lái)的塔逃,這個(gè)參數(shù)有很有幫助讯壶≌饰可以是"line"/"cost"中的一個(gè)悠菜,前者表示只需要給我方法鏈路信息德玫,后者其實(shí)是"line" + "cost"显熏;

  • -l:這個(gè)參數(shù)很有用棘脐,當(dāng)某個(gè)方法很長(zhǎng),那么鏈路追蹤信息打印出來(lái)會(huì)很難看旗扑,你可能只關(guān)心某一行的相關(guān)信息,比如就想看看某一行的代碼執(zhí)行耗時(shí)慈省,以及這一行相關(guān)的變量信息臀防,那么這個(gè)參數(shù)就可以派上用場(chǎng),值就是具體的行號(hào)(對(duì)照源碼);

下面根據(jù)上面的參數(shù)來(lái)實(shí)現(xiàn)不同的觀察功能袱衷,首先是用于測(cè)試的Java類:

public class ReturnTest {

    private TestClass testClass = new TestClass();

    public static void say(int a) {
        int b = a * 10;
        System.out.println("hello:" + b);
        //return b;
    }

    public int getIntVal(int in) {
//        if (in < 7) {
//            System.out.println("in < 7, return");
//            throw new UnsupportedOperationException("test");
//        }

        if (in == 5) {
            String msg = null;
            // produce npe
            in += msg.length();
        }

        long startTime = System.currentTimeMillis() + fibonacci(2);
        String strTag = "the return/throw line test tag";
        if (in < 0) {
            return strTag.charAt(0);
        } else if (in == 0) {
            return 1000;
        }
        // > 0
        if (in < 2) {
            double dbVal = 1.1;
            return (int) (dbVal + 100);
        } else if (in == 2) {
            float fVal = 1.2f;
            return (int) (fVal + 200);
        }
        // > 2
        if (in % 2 == 0) {
            Random random = new Random();
            int rdm = random.nextInt(100);
            if (rdm >= 50) {
                throw new IllegalArgumentException("npe test");
            } else if (rdm <= 20) {
                throw new NullPointerException("< 20");
            }
            // end time
            long end = System.currentTimeMillis();
            long cost = startTime - end;
            int ret = testClass.test(in);
            return (int) (rdm * 10 + ret + (cost / 1000));
        } else {
            ParamModel paramModel = new ParamModel();
            paramModel.setIntVal(in);
            paramModel.setDoubleVal(1.0 * in);
            int subVal = getSubIntVal(paramModel);

            if (subVal == 100) {
                throw new IllegalArgumentException("err occ with in:" + subVal);
            }

            throw new IllegalStateException("error occ with in:" + in);
        }
    }

    /**
     *  不支持遞歸函數(shù)
     *
     * @param n
     * @return
     */
    public int fibonacci(int n) {
        if (n < 0) {
            return -1;
        }
        if (n == 0) {
            return 0;
        }
        if (n <= 2) {
            return 1;
        }
        return fibonacci(n - 1) + fibonacci(n - 2);
    }

    public int getSubIntVal(ParamModel paramModel) {
        if (paramModel == null) {
            return -1;
        }
        if (paramModel.getIntVal() <= 0) {
            return (int) paramModel.getDoubleVal();
        } else if (paramModel.getIntVal() <= 5) {
            return 100;
        } else if (paramModel.getIntVal() <= 8) {
            return 200;
        } else {
            throw new RuntimeException("ill");
        }
    }

    public static void main(String[] args) {
        new Thread(new Runnable() {
            private Random random = new Random();
            private ReturnTest returnTest = new ReturnTest();

            @Override
            public void run() {
                while (true) {
                    try {
                        System.err.println(returnTest.getIntVal(random.nextInt(10)));
                        TimeUnit.MILLISECONDS.sleep(5);
                    } catch (Exception e) {
                        //e.printStackTrace();
                        //System.out.println("error:" + e.getMessage());
                    }
                }
            }
        }).start();
    }
}

public class TestClass {

    Aa aa = new Aa();

    public int test(int in) {

        if (in == 5) {
            return 100;
        }
        String tag = "the in:" + in;
        if (in < 5) {
            in += 2;
        } else {
            in -= 1;
        }

        if (in > 5) {
            throw new IllegalArgumentException("must <= 5");
        }
        if (in <= 3) {
            throw new NullPointerException("must >= 3");
        }

        return in * 100;
    }

}
  • (1)觀察一次方法調(diào)用的執(zhí)行路徑
觀察一次方法調(diào)用執(zhí)行路徑

上面的圖片展示了觀察一次 "ReturnTest.getIntVal"方法調(diào)用的執(zhí)行路徑捎废,本次方法入?yún)⑹?,返回值是201致燥,是從56行代碼退出的登疗,耗時(shí)1ms;

  • (2)在(1)中只要方法被調(diào)用一次篡悟,那么觀察就會(huì)立刻結(jié)束谜叹,所以觀察結(jié)果可能是方法正常結(jié)束匾寝,也可能是拋出了異常搬葬,如果只是希望觀察方法正常退出,那么就可以指定-t參數(shù)為return艳悔,這樣只有當(dāng)?shù)谝淮畏椒ú粧伋霎惓M顺霾艜?huì)結(jié)束觀察急凰;

  • (3)和(2)相反的是,如果你希望監(jiān)控一個(gè)異常的執(zhí)行路徑猜年,比如一個(gè)方法執(zhí)行偶爾會(huì)拋出某種異常抡锈,搞得你很摸不著頭腦,那你就可以指定-t參數(shù)為throw來(lái)觀察拋出異常的執(zhí)行路徑:

觀察方法異常退出執(zhí)行路徑

當(dāng)然乔外,如果你想要觀察的是某種特定的異常床三,可以指定-e參數(shù):

觀察指定的異常
  • (4)自定義輸入?yún)?shù)進(jìn)行方法調(diào)用,并進(jìn)行方法執(zhí)行路徑觀察:需要注意的是杨幼,在執(zhí)行自定義參數(shù)調(diào)用之前撇簿,Java-debug-tool需要獲取到目標(biāo)對(duì)象,或者目標(biāo)方法是一個(gè)靜態(tài)方法差购,否則Java-debug-tool命令會(huì)一直等待獲取目標(biāo)對(duì)象(不會(huì)主動(dòng)創(chuàng)建目標(biāo)對(duì)象)
觀察特定輸入
  • (5)記錄方法調(diào)用請(qǐng)求
錄制方法請(qǐng)求

錄制完成后可以回放請(qǐng)求:

觀察回放請(qǐng)求
  • (6)觀察特定輸入執(zhí)行路徑
觀察特定參數(shù)-1
觀察特定參數(shù)-2

redefineClass命令

redefineClass命令用于替換一個(gè)類的字節(jié)碼四瘫,可以簡(jiǎn)寫(xiě)成rdf,用于快速恢復(fù)生產(chǎn)環(huán)境欲逃,命令的參數(shù)沒(méi)有mt命令復(fù)雜找蜜,但是需要有幾點(diǎn)需要注意:

  • 一個(gè)client對(duì)一個(gè)類執(zhí)行rdf命令,就會(huì)鎖定這個(gè)類稳析,其他client就不能對(duì)相同的類執(zhí)行rdf洗做,直至client退出;

我們把getIntVal方法的開(kāi)始部分的注釋去掉彰居,也就是:

//        if (in < 7) {
//            System.out.println("in < 7, return");
//            throw new UnsupportedOperationException("test");
//        }

這一段內(nèi)容诚纸,去掉之后,只要輸入的參數(shù)小于7裕菠,那么就會(huì)拋出異常咬清,我們使用mt命令配合custom來(lái)驗(yàn)證我們的rdf結(jié)果:

執(zhí)行rdf命令

可以看到,此時(shí)輸入?yún)?shù)為5的時(shí)候拋出了那個(gè)期望的異常;

rollback命令

rollback命令用于將一個(gè)增強(qiáng)過(guò)的類恢復(fù)到初始狀態(tài)旧烧,可以使用back簡(jiǎn)寫(xiě)影钉,目前僅支持恢復(fù)到初始狀態(tài),后續(xù)會(huì)記錄增強(qiáng)stage掘剪,然后恢復(fù)到上一次增強(qiáng)過(guò)的字節(jié)碼:

回滾一個(gè)類

findClass命令

是不是曾經(jīng)出現(xiàn)過(guò)因?yàn)閖ar包沖突導(dǎo)致的類加載錯(cuò)誤的情況呢平委?findClass命令用于快速判斷一個(gè)類是不是在目標(biāo)JVM加載了,如果加載了夺谁,是從哪個(gè)jar包中加載的(jar一般都會(huì)有版本號(hào)廉赔,可以看看是不是從期望的jar版本中加載的),是被什么類加載器加載的匾鸥,還可以僅僅使用類名(不含包名)來(lái)匹配蜡塌,甚至通過(guò)正則表達(dá)式來(lái)匹配,可以使用簡(jiǎn)寫(xiě)fc:

查找類信息

help命令

如果你不知道怎么使用一個(gè)命令勿负,那么可以試試help命令:

help命令

如何重復(fù)發(fā)生上一次發(fā)送的命令

有時(shí)候需要重復(fù)上一次輸入的命令馏艾,但是上一次命令輸入內(nèi)容很多,如何快速實(shí)現(xiàn)上一次命令的復(fù)制呢奴愉?下面的一些字符可以快速幫你搞定這件事情:"p","r","s","go","last"

重復(fù)命令發(fā)送

后續(xù)將支持命令歷史記錄回放的功能琅摩,目前僅支持回放上一次輸入。

規(guī)劃中的功能

  • (1)線程相關(guān)功能锭硼,包括當(dāng)前線程總數(shù)房资、活動(dòng)線程數(shù)等等信息,并能獲取到某個(gè)線程的調(diào)用堆棧等信息檀头;
  • (2)GC相關(guān)信息轰异;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鳖擒,隨后出現(xiàn)的幾起案子溉浙,更是在濱河造成了極大的恐慌,老刑警劉巖蒋荚,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件戳稽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡期升,警方通過(guò)查閱死者的電腦和手機(jī)惊奇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)播赁,“玉大人颂郎,你說(shuō)我怎么就攤上這事∪菸” “怎么了乓序?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵寺酪,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我替劈,道長(zhǎng)寄雀,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任陨献,我火速辦了婚禮盒犹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘眨业。我一直安慰自己急膀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布龄捡。 她就那樣靜靜地躺著卓嫂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪墅茉。 梳的紋絲不亂的頭發(fā)上命黔,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天呜呐,我揣著相機(jī)與錄音就斤,去河邊找鬼。 笑死蘑辑,一個(gè)胖子當(dāng)著我的面吹牛洋机,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播洋魂,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼绷旗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了副砍?” 一聲冷哼從身側(cè)響起衔肢,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎豁翎,沒(méi)想到半個(gè)月后角骤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡心剥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年邦尊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片优烧。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蝉揍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畦娄,到底是詐尸還是另有隱情又沾,我是刑警寧澤弊仪,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站杖刷,受9級(jí)特大地震影響撼短,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜挺勿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一曲横、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧不瓶,春花似錦禾嫉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至麦备,卻和暖如春孽椰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凛篙。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工黍匾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呛梆。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓锐涯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親填物。 傳聞我的和親對(duì)象是個(gè)殘疾皇子纹腌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容