java性能調優及問題追蹤--Btrace的使用


在生產環境中經常遇到格式各樣的問題,如OOM或者莫名其妙的進程死掉。一般情況下是通過修改程序,添加打印日志;然后重新發布程序來完成。然而,這不僅麻煩,而且帶來很多不可控的因素。有沒有一種方式,在不修改原有運行程序的情況下獲取運行時的數據信息呢?如方法參數、返回值、全局變量、堆棧信息等。Btrace就是這樣一個工具,它可以在不修改原有代碼的情況下動態地追蹤java運行程序,通過hotswap技術,動態將跟蹤字節碼注入到運行類中,對運行代碼侵入較小,對性能上的影響可以忽略不計。

基礎說明

由於Btrace會把腳本邏輯直接侵入到運行的代碼中,所以在使用上做很多限制:

  • 不能創建對象
  • 不能使用數組
  • 不能拋出或捕獲異常
  • 不能使用循環
  • 不能使用synchronized關鍵字
  • 屬性和方法必須使用static修飾

需要特別注意的是:不恰當的使用BTrace可能導致JVM崩潰,如在BTrace腳本使用錯誤的class文件,所以在上生產環境之前,務必在本地充分的驗證腳本的正確性。

Btrace可以做什么?

  • 接口性能變慢,分析每個方法的耗時情況;
  • 當在Map中插入大量數據,分析其擴容情況;
  • 分析哪個方法調用了System.gc(),調用棧如何;
  • 執行某個方法拋出異常時,分析運行時參數;
  • ....

參數說明:

指定分析方法的入口:@OnMethod

Btrace使用@OnMethod注解定義需要分析的方法入口

@OnMethod注解中,需要指定class、method以及location等,class表明需要監控的類,method表明需要監控的方法,指定方式如下:

  • 使用全限定名:clazz="com.metty.rpc.common.BtraceCase", method="add"
  • 使用正則表達式:clazz="/javax.swing../", method="/./"
  • 使用接口:clazz="+com.ctrip.demo.Filter", method="doFilter"
  • 使用注解:clazz="@javax.jws.WebService", method=""@javax.jws.WebMethod"
  • 如果需要分析構造方法,需要指定method=" "

指定方法攔截的位置:@Location

定義Btrace對方法的攔截位置,通過@Location注解指定,默認為Kind.ENTRY

  • Kind.ENTRY:在進入方法時,調用Btrace腳本
  • Kind.RETURN:方法執行完時,調用Btrace腳本,只有把攔截位置定義為Kind.RETURN,才能獲取方法的返回結果@Return和執行時間@Duration
  • Kind.CALL:分析方法中調用其它方法的執行情況,比如在execute方法中,想獲取add方法的執行耗時,必須把where設置成Where.AFTER
  • Kind.LINE:通過設置line,可以監控代碼是否執行到指定的位置
  • Kind.ERROR, Kind.THROW, Kind.CATCH

總結

Btrace能做的事情太多,但使用之前切記檢查腳本的可行性,一旦Btrace腳本侵入到系統中,只有通過重啟才能恢復。


通過jvisualvm插件的方式進行測試:

安裝Btrace插件

工具-->插件-->可用插件中找到BTrace Workbench進行安裝即可。

測試用例

package com.vmtools;

public class Counter {
    // 總數
    private static int totalCount = 0;

    public int add(int num) throws Exception {
        totalCount += num;
        sleep();

        return totalCount;
    }

    private void sleep() throws InterruptedException {
        Thread.sleep(1000);
    }
}

package com.vmtools;

import java.util.Random;
public class BtraceTest {

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

        Random random = new Random();

        // 計數器
        Counter counter = new Counter();
        while (true) {
            // 每次增加隨機值
            counter.add(random.nextInt(10));
            Thread.sleep(1000);
        }
    }
}


Btrace測試

運行上訴測試用例

jvisualvm中找到對應的進程id-->Trace application...-->分別進行相應的測試

獲取add()方法參數值和返回值。

/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

//獲取add()方法參數值和返回值。
@BTrace
public class TracingScript {
	/* put your code here */
    @OnMethod(
        clazz="com.vmtools.Counter",
        method="add",
        location=@Location(Kind.RETURN)
    )

    public static void func(
            int a,
            @Return int result) {
        println("trace: =======================");
        jstack();
        println(strcat("a:", str(a)));
        println(strcat("result:", str(result)));
    }
}

定時獲取Counter類的屬性值totalCount

/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

//定時獲取Counter類的屬性值totalCount。
@BTrace
public class TracingScript {
    private static Object totalCount=0;

	/* put your code here */
    @OnMethod(
        clazz="com.vmtools.Counter",
        method="add",
        location=@Location(Kind.RETURN)
    )
    public static void func(@Self com.vmtools.Counter counter) {
        totalCount = get(field("com.vmtools.Counter", "totalCount"), counter);
    }

    @OnTimer(2000)
    public static void print(){
        println(" ====== ");
        println(strcat("totalCount: ",str(totalCount)));
    }
}

獲取add方法執行時間

/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

//獲取add方法執行時間
@BTrace
public class TracingScript {
    @TLS private static long startTime = 0;

	/* put your code here */
    @OnMethod(
        clazz="com.vmtools.Counter",
        method="add"
    )
    public static void func(@Self com.vmtools.Counter counter) {
        startTime = timeNanos();
    }

    @OnMethod(
        clazz="com.vmtools.Counter",
        method="add",
        location=@Location(Kind.RETURN)
    )
    public static void endExecute(@Duration long duration){  
     long time = timeNanos() - startTime;  
     println(strcat("execute time(nanos): ", str(time)));  
     println(strcat("duration(nanos): ", str(duration)));  
   }   
}

參考文檔:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM