Java線上問(wèn)題排查神器Arthas實(shí)戰(zhàn)原理解析
概述
背景
是不是在實(shí)際開(kāi)發(fā)工作當(dāng)中經(jīng)常碰到自己寫(xiě)的代碼在開(kāi)發(fā)、測(cè)試環(huán)境行云流水穩(wěn)得一筆,可一到線上就經(jīng)常不是缺這個(gè)就是少那個(gè)反正就是一頓報(bào)錯(cuò)抽風(fēng)似的,線上調(diào)試代碼又很麻煩,讓人頭疼得抓狂;而且debug不一定是最高效的方法,遇到線上問(wèn)題不能debug了怎么辦。原先我們Java中我們常用分析問(wèn)題一般是使用JDK自帶或第三方的分析工具如jstat、jmap、jstack、 jconsole、visualvm、Java Mission Control、MAT等。但此刻的你沒(méi)有看錯(cuò),還有一款神器Arthas工具著實(shí)讓人吃驚,可幫助程序員解決很多繁瑣的問(wèn)題,使得加班解決線上問(wèn)題成為過(guò)去的可能性大大提高。
定義
Arthas是一個(gè)Java診斷工具,由阿里巴巴中間件團(tuán)隊(duì)開(kāi)源,目前已在Java開(kāi)發(fā)人員中被廣泛采用。Arthas能夠分析,診斷,定位Java應(yīng)用問(wèn)題,例如:JVM信息,線程信息,搜索類(lèi)中的方法,跟蹤代碼執(zhí)行,觀測(cè)方法的入?yún)⒑头祷貐?shù)等等。并能在不修改應(yīng)用代碼的情況下,對(duì)業(yè)務(wù)問(wèn)題進(jìn)行診斷,包括查看方法的出入?yún)?,異常,監(jiān)測(cè)方法執(zhí)行耗時(shí),類(lèi)加載信息等,大大提升線上問(wèn)題排查效率。簡(jiǎn)單的話:就是再不重啟應(yīng)用的情況下達(dá)到排查問(wèn)題的目的。
特性
- 儀表盤(pán)實(shí)時(shí)查看系統(tǒng)的運(yùn)行狀態(tài)。
- OGNL表達(dá)式查看參數(shù)和返回值/例外,查看方法參數(shù)、返回值和異常。
- 通過(guò)jad/sc/redefine實(shí)現(xiàn)在線熱插拔。
- 快速解決類(lèi)沖突問(wèn)題,定位類(lèi)加載路徑。
- 快速定位應(yīng)用熱點(diǎn)和生成火焰圖。
- 支持在線診斷WebConsole。
- Arthas對(duì)應(yīng)用程序沒(méi)有侵入(但對(duì)宿主機(jī)jvm有侵入),代碼或項(xiàng)目中不需要引入jar包或依賴,因?yàn)槭峭ㄟ^(guò)attach的機(jī)制實(shí)現(xiàn)的,我們的應(yīng)用的程序和arthas都是獨(dú)立的進(jìn)程,arthas是通過(guò)和jvm底層交互來(lái)獲取運(yùn)行在其上的應(yīng)用程序?qū)崟r(shí)數(shù)據(jù)的,靈活查看運(yùn)行時(shí)的值,這個(gè)和hickwall,jprofiler等監(jiān)控軟件的區(qū)別(JPofiler也有這樣的功能,但是是收費(fèi)的)動(dòng)態(tài)增加aop代理和監(jiān)控日志功能,無(wú)需重啟服務(wù),而且關(guān)閉arthas客戶端后會(huì)還原所有增強(qiáng)過(guò)的類(lèi),原則上是不會(huì)影響現(xiàn)有業(yè)務(wù)邏輯的。
- 對(duì)應(yīng)用程序所在的服務(wù)器性能的影響,個(gè)別命令使用不當(dāng)?shù)脑挘赡軙?huì)撐爆jvm內(nèi)存或?qū)е聭?yīng)用程序響應(yīng)變慢,命令的輸出太多,接口調(diào)用太頻繁會(huì)記錄過(guò)多的數(shù)據(jù)變量到內(nèi)存里,比如tt指令,建議加 -n 參數(shù) 限制輸出次數(shù),sc * 通配符的使用不當(dāng),范圍過(guò)大,使用異步任務(wù)時(shí),請(qǐng)勿同時(shí)開(kāi)啟過(guò)多的后臺(tái)異步命令,以免對(duì)目標(biāo)JVM性能造成影響,一把雙刃劍(它甚至可以修改jdk里的原生類(lèi)),所以在線上運(yùn)行肯定是需要權(quán)限和流程控制的。
使用場(chǎng)景
在日常開(kāi)發(fā)中,當(dāng)我們發(fā)現(xiàn)應(yīng)用的某個(gè)接口響應(yīng)比較慢,這個(gè)時(shí)候想想要分析一下原因,找到代碼中耗時(shí)的部分,比較容易想到的是在接口鏈路的 IO 操作上下游打印時(shí)間日志,再根據(jù)幾個(gè)時(shí)間點(diǎn)的日志算出耗時(shí)長(zhǎng)的 IO 操作。這種方式?jīng)]有問(wèn)題,但是加日志需要發(fā)布,既繁瑣又低效,這個(gè)時(shí)候可以引入一些線上 debug 的工具,arthas 就是很好的一種,除了分析耗時(shí),還可以打印調(diào)用棧、方法入?yún)⒓胺祷兀?lèi)加載情況,線程池狀態(tài),系統(tǒng)參數(shù)等等,其實(shí)現(xiàn)原理是解析 JVM 在操作系統(tǒng)中的文件,大部分操作是只讀的,對(duì)服務(wù)進(jìn)程沒(méi)有侵入性,因此可以放心使用。
實(shí)戰(zhàn)
CPU占用高示例
創(chuàng)建一個(gè)springboot項(xiàng)目并打包成arthas-demo-1.0.jar,啟動(dòng)arthas-demo-1.0.jar
代碼示例如下
package cn.itxs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@SpringBootApplication
public class App
{
public static void main(String[] args) {
SpringApplication.run(App.class,args);
new Thread( () -> {
while (true) {
String str = UUID.randomUUID().toString().replaceAll("-", "");
}
},"cpu demo thread").start();
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
},"cpu with sleep thread").start();
}
}安裝與使用
推薦方式
# 下載`arthas-boot.jar`這種也是官方推薦的方式 curl -O https://arthas.aliyun.com/arthas-boot.jar # 啟動(dòng)arthas-boot.jar,必須啟動(dòng)至少一個(gè) java程序,否則會(huì)自動(dòng)退出。運(yùn)行此命令會(huì)自動(dòng)發(fā)現(xiàn) java進(jìn)程,輸入需要 attach 進(jìn)程對(duì)應(yīng)的序列號(hào),例如,輸入1按回車(chē)則會(huì)監(jiān)聽(tīng)該進(jìn)程。 java -jar arthas-boot.jar # 比如輸入JVM (jvm實(shí)時(shí)運(yùn)行狀態(tài),內(nèi)存使用情況等)

CPU占用高示例
package cn.itxs.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/thread")
public class ThreadController {
private Object obj1 = new Object();
private Object obj2 = new Object();
@RequestMapping("/test")
@ResponseBody
public String test(){
new Thread(() -> {
synchronized (obj1){
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
synchronized (obj2){
System.out.printf("thread 1執(zhí)行到此");
}
}
}
},"thread 1").start();
synchronized (obj2) {
synchronized (obj1){
System.out.printf("thread 2執(zhí)行到此");
},"thread 2").start();
return "thread test";
}
}SpringBoot啟動(dòng)類(lèi)
package cn.itxs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App
{
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}# 訪問(wèn)頁(yè)面http://192.168.50.100:8080/cpu/test
# 儀表盤(pán)命令,通過(guò)上面我們可以發(fā)現(xiàn)線程ID為29也即是線程名稱為“cpu demo thread”占用的cpu較高
dashboard

第一部分時(shí)顯示JVM中運(yùn)行的所有線程:所在線程組,優(yōu)先級(jí),線程的狀態(tài),CPU的占有率,是否是后臺(tái)進(jìn)程等。
第二部分顯示的JVM內(nèi)存的使用情況和GC的信息。
第三部分是操作系統(tǒng)的一些信息和 Java版本號(hào)。
# 當(dāng)前最忙的前N個(gè)線程 thread -b, ##找出當(dāng)前阻塞其他線程的線程 thread -n 5 -i 1000 #間隔一定時(shí)間后展示,本例中可以看到最忙CPU線程為id=45,代碼行數(shù)為19 thread -n 5 # jad查看反編譯的代碼 jad cn.itxs.controller.CpuController


線程死鎖示例
package cn.itxs.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/thread")
public class ThreadController {
private Object obj1 = new Object();
private Object obj2 = new Object();
@RequestMapping("/test")
@ResponseBody
public String test(){
new Thread(() -> {
synchronized (obj1){
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
}
synchronized (obj2){
System.out.println("thread 1執(zhí)行到此");
}
},"thread 1").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
synchronized (obj1){
System.out.println("thread 2執(zhí)行到此");
},"thread 2").start();
return "thread test";
}
}# 啟動(dòng)SpringBoot演示程序,訪問(wèn)頁(yè)面http://192.168.50.100:8080/thread/test # 運(yùn)行arthas,查看線程 thread
# 查看阻塞線程 thread -b # jad反編譯查看代碼 jad --source-only cn.itxs.controller.ThreadController

線上修復(fù)熱部署
準(zhǔn)備一個(gè)有問(wèn)題的java類(lèi)
package cn.itxs.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/hot")
public class HotController {
@RequestMapping("/test")
@ResponseBody
public String test(){
boolean flag = true;
if (flag) {
System.out.println("開(kāi)始處理邏輯");
throw new RuntimeException("出異常了");
}
System.out.println("結(jié)束流程");
return "hot test";
}
}啟動(dòng)Spring Boot程序,訪問(wèn)頁(yè)面http://192.168.50.94:8080/hot/test發(fā)現(xiàn)報(bào)錯(cuò),當(dāng)我們分析到這段程序出異常后,我們分析異常后進(jìn)行線上代碼修改不停機(jī)不重新發(fā)包的情況下的熱更新,操作流程如下:
# 第一步:`jad命令` 將需要更改的文件先進(jìn)行反編譯,保存下來(lái) ,編譯器修改,-c 指定這個(gè)類(lèi)的classloader的哈希值,–source-only只顯示源碼,最后是文件反編譯之后的存放路徑
jad --source-only cn.itxs.controller.HotController > /home/commons/arthas/data/HotController.java
# 我們將HotController.java中的throw new RuntimeException("出異常了")代碼刪掉,修改完后需要將類(lèi)重新加載到JVM
# 第二步:`SC命令` 查找當(dāng)前類(lèi)是哪個(gè)classLoader加載的,首先,使用sc命令找到要修改的類(lèi).sc全稱-search class, -d表示detail,主要是為了獲取classLoader的hash值
sc -d *HotController | grep classLoader
classLoaderHash 6267c3bb #類(lèi)加載器 編號(hào)
# 第三步:`MC命令` 用指定的classloader重新將類(lèi)在內(nèi)存中編譯
mc -c 6267c3bb /home/commons/arthas/data/HotController.java -d /home/commons/arthas/class
# 第四步:`redefine命令` 將編譯后的類(lèi)加載到JVM,參數(shù)是編譯后的.class文件地址
redefine /home/commons/arthas/class/cn/itxs/controller/HotController.class 以上操作后再次訪問(wèn)一下頁(yè)面http://192.168.50.94:8080/hot/test,發(fā)現(xiàn)異常沒(méi)有了程序已經(jīng)是我們修改正確后的,class文件替換成功,功能確實(shí)很強(qiáng)大。

上面我們是手工一步步執(zhí)行,當(dāng)然我們可以使用shell腳本串起來(lái)簡(jiǎn)單操作。
此外還可以安裝Alibaba Cloud Toolkit熱部署組件(一鍵retransform),熱部署組件支持一鍵將編輯器中修改的 Java 源碼快速編譯,并更新到遠(yuǎn)端應(yīng)用服務(wù)中,免去手動(dòng) dump、mc 的過(guò)程。此外,也可以一鍵還原 retransform 的類(lèi)文件。

由于Arthas命令還是較復(fù)雜,Arthas-idea插件(部分命令可視化)是一個(gè)幫助生成命令的IDEA插件,使用文檔:https://www.yuque.com/arthas-idea-plugin;
安裝基于Arthas實(shí)現(xiàn)的簡(jiǎn)單好用的熱部署插件ArthasHotSwap可以一鍵生成熱部署命令,提高我們線上維護(hù)的效率。

線上問(wèn)題常見(jiàn)定位
watch(方法執(zhí)行數(shù)據(jù)觀測(cè))
package cn.itxs.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@RestController
@RequestMapping("/watch")
public class WatchController {
private static Random random = new Random();
private int illegalArgumentCount = 0;
@RequestMapping("/test")
@ResponseBody
public String test(){
String res = null;
try {
int number = random.nextInt() / 10000;
List<Integer> idStrs = this.getIdStr(number);
res = printList(number, idStrs);
}
catch (Exception e) {
System.out.println(String.format("illegalArgumentCount:%3d, ", this.illegalArgumentCount) + e.getMessage());
return res;
}
private List<Integer> getIdStr(int number) {
if (number < 5) {
++this.illegalArgumentCount;
throw new IllegalArgumentException("number is: " + number + ", need >= 5");
ArrayList<Integer> result = new ArrayList<Integer>();
int count = 2;
while (count <= number) {
if (number % count == 0) {
result.add(count);
number /= count;
count = 2;
continue;
}
++count;
return result;
private String printList(int number, List<Integer> primeFactors) {
StringBuffer sb = new StringBuffer(number + "=");
for (int factor : primeFactors) {
sb.append(factor).append('*');
if (sb.charAt(sb.length() - 1) == '*') {
sb.deleteCharAt(sb.length() - 1);
System.out.println(sb);
return sb.toString();
}啟動(dòng)Spring Boot程序,通過(guò)Jmeter每秒訪問(wèn)一次http://192.168.50.100:8080/watch/test

# Arthas中的**watch**命令可以讓我們能方便的觀察到指定方法的調(diào)用情況,可以觀察到返回值,入?yún)?,以及變量等?
# watch 全路徑類(lèi)名 方法名 觀察表達(dá)式 -x 3 ,觀察表達(dá)式匹配ognl表達(dá)式,觀察的維度也比較多。
# 比如:watch cn.itxs.controller.WatchController printList "{params,returnObj}" -x 3
# 查看printList方法的入?yún)⒑统鰠ⅲ?x表示的是遍歷結(jié)果深度默認(rèn)1,只會(huì)打印對(duì)象的堆地址,看不到具體的屬性值,-x 2會(huì)打印結(jié)果的屬性值的信息 -x 3會(huì)輸出入?yún)傩灾岛徒Y(jié)果屬性值
# -n 1只抓緊一次,由于我們這里是模擬一直請(qǐng)求的
watch cn.itxs.controller.WatchController printList '{params}' -n 1
# -x 表示的是遍歷結(jié)果深度默認(rèn)3
watch cn.itxs.controller.WatchController printList '{params}' -n 1 -x 3
# params[0]代表第一個(gè)參數(shù)
watch cn.itxs.controller.WatchController printList '{params[0]}' -n 1 -x 3
# 方法的返回值
watch cn.itxs.controller.WatchController getIdStr '{returnObj}' -n 1 -x 3
# 方法參數(shù)和返回值
watch cn.itxs.controller.WatchController getIdStr '{params,returnObj}' -n 1 -x 3
# 觀察方法執(zhí)行前后當(dāng)前對(duì)象屬性值 watch cn.itxs.controller.WatchController getIdStr 'target.illegalArgumentCount'

# 觀察異常信息,觀察表達(dá)式里增加throwExp就好了。如果增加-e 參數(shù)就能過(guò)濾掉非異常的監(jiān)聽(tīng)了。

# 在觀察表達(dá)式后面,我們可以增加條件表達(dá)式,例如按時(shí)間過(guò)濾:#cost>0.5,單位是毫秒,那么控制臺(tái)輸出來(lái)的都是耗時(shí)在0.5毫秒以上的方法調(diào)用
watch cn.itxs.controller.WatchController getIdStr '{params}' '#cost>0.5'
# 按條件過(guò)濾觀察params[1].size>4:這里支持ognl表達(dá)式。下面例子的意思是:第二個(gè)參數(shù)(也就是List primeFactors),的size大于4的時(shí)候才觀察入?yún)?。watch cn.itxs.controller.WatchController printList '{params}' 'params[1].size>4' -x 3
monitor(方法執(zhí)行監(jiān)控)
monitor結(jié)果包括如下
- timestamp:時(shí)間戳
- class:Java類(lèi)
- method:方法(構(gòu)造方法、普通方法)
- total:調(diào)用次數(shù)
- success:成功次數(shù)
- fail:失敗次數(shù)
- rt:平均RT
- fail-rate:失敗率
# -c :統(tǒng)計(jì)周期,默認(rèn)值為10秒 monitor -c 10 cn.itxs.controller.WatchController getIdStr # 在方法調(diào)用之前計(jì)算condition-express,方法后可帶表達(dá)式 monitor -b -c 10 cn.itxs.controller.WatchController getIdStr


trace
# trace:方法內(nèi)部調(diào)用路徑,并輸出方法路徑上的每個(gè)節(jié)點(diǎn)上耗時(shí) trace cn.itxs.controller.WatchController test -n 2 #包含jdk的函數(shù)--skipJDKMethod <value> skip jdk method trace, default value true.默認(rèn)情況下,trace不會(huì)包含jdk里的函數(shù)調(diào)用,如果希望trace jdk里的函數(shù),需要顯式設(shè)置--skipJDKMethod false。 trace --skipJDKMethod false cn.itxs.controller.WatchController test -n 2 # 調(diào)用耗時(shí)過(guò)濾,只會(huì)展示耗時(shí)大于10ms的調(diào)用路徑,有助于在排查問(wèn)題的時(shí)候,只關(guān)注異常情況 trace cn.itxs.controller.WatchController test '#cost > 1'

stack
# 輸出當(dāng)前方法被調(diào)用的調(diào)用路徑,getIdStr是從test方法調(diào)用進(jìn)來(lái)的 stack cn.itxs.controller.WatchController getIdStr -n 1 # 輸出當(dāng)前方法被調(diào)用的調(diào)用路徑,條件表達(dá)過(guò)濾,第0個(gè)參數(shù)小于0,也可以根據(jù)執(zhí)行時(shí)間來(lái)過(guò)濾,'#cost>1' stack cn.itxs.controller.WatchController getIdStr 'params[0]<0' -n 1


tt
tt(TimeTunnel):方法執(zhí)行數(shù)據(jù)的時(shí)空隧道,記錄下指定方法每次調(diào)用的入?yún)⒑头祷匦畔?,并能?duì)這些不同的時(shí)間下調(diào)用進(jìn)行觀測(cè)。對(duì)于一個(gè)最基本的使用來(lái)說(shuō),就是記錄下當(dāng)前方法的每次調(diào)用環(huán)境現(xiàn)場(chǎng)。
# 記錄指定方法的每次調(diào)用環(huán)境現(xiàn)場(chǎng) tt -t cn.itxs.controller.WatchController getIdStr # 列出所有調(diào)用記錄 tt -l cn.itxs.controller.WatchController getIdStr

# 篩選調(diào)用記錄 tt -s 'method.name=="getIdStr"' # 查看調(diào)用信息 tt -i 1001

# 重新發(fā)起一次調(diào)用 tt -i 1001 -p

Web Console
# 啟動(dòng)時(shí)指定Linux的ip java -jar arthas-boot.jar --target-ip 192.168.50.94

profiler
profiler 命令支持生成應(yīng)用熱點(diǎn)的火焰圖。本質(zhì)上是通過(guò)不斷的采樣,然后把收集到的采樣結(jié)果生成火焰圖。一般分析性能可以先通過(guò)Arthas profiler命令生成jfr文件;在本地通過(guò)jprofiler來(lái)分析jfr文件,定位誰(shuí)在調(diào)用我。
# 啟動(dòng)profiler 默認(rèn)情況下,生成的是cpu的火焰圖,即event為cpu。可以用--event參數(shù)來(lái)指定 profiler start # 獲取已采集的sample的數(shù)量 profiler getSamples # 查看profiler狀態(tài) profiler status # 停止profiler 生成html格式結(jié)果,默認(rèn)情況下,結(jié)果文件是html格式,也可以用--format參數(shù)指定;或者在--file參數(shù)里用文件名指名格式。比如--file /tmp/result.html profiler stop --format html

通過(guò)瀏覽器查看arthas-output下面的profiler結(jié)果,http://192.168.50.100:3658/arthas-output/
# profiler支持的events profiler list

# 可以用--event參數(shù)指定要采樣的事件,比如對(duì)alloc事件進(jìn)入采樣: profiler start --event alloc # 使用execute來(lái)執(zhí)行復(fù)雜的命令 profiler execute 'start,framebuf=5000000' # 生成 jfr格式結(jié)果;注意,jfr只支持在 start時(shí)配置。如果是在stop時(shí)指定,則不會(huì)生效。 profiler start --file /tmp/test.jfr # 配置 include/exclude 來(lái)過(guò)濾數(shù)據(jù) profiler start --include 'java/*' --include 'demo/*' --exclude '*Unsafe.park*' # profiler執(zhí)行 300 秒自動(dòng)結(jié)束,可以用 -d/--duration 參數(shù)指定 profiler start --duration 300

其他功能
提供Http API可以提供結(jié)構(gòu)化的數(shù)據(jù),支持更復(fù)雜的交互功能,比如特定應(yīng)用場(chǎng)景的一系列診斷操作。Http API接口地址為:http://ip:port/api,必須使用POST方式提交請(qǐng)求參數(shù)。如POST http://127.0.0.1:8563/api
{
"action": "exec",
"requestId": "req112",
"sessionId": "94766d3c-8b39-42d3-8596-98aee3ccbefb",
"consumerId": "955dbd1325334a84972b0f3ac19de4f7_2",
"command": "version",
"execTimeout": "10000"
}docker使用,很多時(shí)候,應(yīng)用在docker里出現(xiàn)arthas無(wú)法工作的問(wèn)題,是因?yàn)閼?yīng)用沒(méi)有安裝 JDK ,而是安裝了 JRE 。如果只安裝了 JRE,則會(huì)缺少很多JAVA的命令行工具和類(lèi)庫(kù),Arthas也沒(méi)辦法正常工作,可以使用公開(kāi)的JDK鏡像和包管理軟件來(lái)安裝這兩種方式在Docker里使用JDK。
# 選擇需要監(jiān)控應(yīng)用的進(jìn)程編號(hào),回車(chē)后Arthas會(huì)attach到目標(biāo)進(jìn)程上,并輸出日志: docker exec -it arthas-demo /bin/sh -c "java -jar /opt/arthas/arthas-boot.jar" # 甚至我們可以直接把a(bǔ)rthas放到容器鏡像文件中:
Arthas Spring Boot Starter:應(yīng)用啟動(dòng)后,spring會(huì)啟動(dòng)arthas,并且attach自身進(jìn)程。
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-spring-boot-starter</artifactId>
<version>${arthas.version}</version>
</dependency>非spring boot應(yīng)用使用方式
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-agent-attach</artifactId>
<version>${arthas.version}</version>
</dependency>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-packaging</artifactId>
<version>${arthas.version}</version>
</dependency>import com.taobao.arthas.agent.attach.ArthasAgent;
public class ArthasAttachExample {
public static void main(String[] args) {
ArthasAgent.attach();
}
}到此這篇關(guān)于Java線上問(wèn)題排查神器Arthas實(shí)戰(zhàn)分析的文章就介紹到這了,更多相關(guān)Java線上問(wèn)題排查神器Arthas內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java抓取12306信息實(shí)現(xiàn)火車(chē)余票查詢示例
這篇文章主要介紹了java抓取12306信息實(shí)現(xiàn)火車(chē)余票查詢示例,需要的朋友可以參考下2014-04-04
Java數(shù)據(jù)類(lèi)型Integer與int的區(qū)別詳細(xì)解析
這篇文章主要介紹了Java數(shù)據(jù)類(lèi)型Integer與int的區(qū)別詳細(xì)解析,Ingeter是int的包裝類(lèi),int的初值為0,Ingeter的初值為null,int和integer(無(wú)論new否)比,都為true,因?yàn)闀?huì)把Integer自動(dòng)拆箱為int再去比,需要的朋友可以參考下2023-12-12
普通類(lèi)注入不進(jìn)spring bean的解決方法
這篇文章主要介紹了普通類(lèi)注入不進(jìn)spring bean的解決方法,幫助大家更好的理解和使用spring bean,感興趣的朋友可以了解下2021-01-01
Java?DelayQueue實(shí)現(xiàn)延時(shí)任務(wù)的示例詳解
DelayQueue是一個(gè)無(wú)界的BlockingQueue的實(shí)現(xiàn)類(lèi),用于放置實(shí)現(xiàn)了Delayed接口的對(duì)象,其中的對(duì)象只能在其到期時(shí)才能從隊(duì)列中取走。本文就來(lái)利用DelayQueue實(shí)現(xiàn)延時(shí)任務(wù),感興趣的可以了解一下2022-08-08
SpringBoot整合RabbitMQ及生產(chǎn)全場(chǎng)景高級(jí)特性實(shí)戰(zhàn)
本文主要介紹了SpringBoot整合RabbitMQ及生產(chǎn)全場(chǎng)景高級(jí)特性實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10
java Map轉(zhuǎn)Object與Object轉(zhuǎn)Map實(shí)現(xiàn)代碼
這篇文章主要介紹了 java Map轉(zhuǎn)Object與Object轉(zhuǎn)Map實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02

