主頁 > 後端開發 > Arthas線上監控及問題定位

Arthas線上監控及問題定位

2020-09-11 08:53:23 後端開發

前言

在使用 Arthas 之前,當遇到 Java 線上問題時,如 CPU 飆升、負載突高、記憶體溢位等問題,你需要查命令,查網路,然后 jps、jstack、jmap、jhat、jstat、hprof 等一通操作,最終焦頭爛額,還不一定能查出問題所在,而現在,大多數的常見問題你都可以使用 Arthas 輕松定位,迅速解決,及時止損,準時下班,

1、Arthas 介紹

ArthasAlibaba 在 2018 年 9 月開源的 Java 診斷工具,支持 JDK6+, 采用命令列互動模式,提供 Tab 自動不全,可以方便的定位和診斷線上程式運行問題,截至本篇文章撰寫時,已經識訓 Star 17000+,

Arthas 官方檔案十分詳細,本文也參考了官方檔案內容,同時在開源在的 Github 的專案里的 Issues 里不僅有問題反饋,更有大量的使用案例,也可以進行學習參考,

開源地址:https://github.com/alibaba/arthas

官方檔案:https://arthas.aliyun.com/doc/

2、Arthas 使用場景

得益于 Arthas 強大且豐富的功能,讓 Arthas 能做的事情超乎想象,下面僅僅列舉幾項常見的使用情況,更多的使用場景可以在熟悉了 Arthas 之后自行探索,

  1. 是否有一個全域視角來查看系統的運行狀況?
  2. 為什么 CPU 又升高了,到底是哪里占用了 CPU ?
  3. 運行的多執行緒有死鎖嗎?有阻塞嗎?
  4. 程式運行耗時很長,是哪里耗時比較長呢?如何監測呢?
  5. 這個類從哪個 jar 包加載的?為什么會報各種類相關的 Exception?
  6. 我改的代碼為什么沒有執行到?難道是我沒 commit?分支搞錯了?
  7. 遇到問題無法在線上 debug,難道只能通過加日志再重新發布嗎?
  8. 有什么辦法可以監控到 JVM 的實時運行狀態?

3、Arthas 怎么用

前文已經提到,Arthas 是一款命令列互動模式的 Java 診斷工具,由于是 Java 撰寫,所以可以直接下載相應 的 jar 包運行,

3.1 安裝

可以在官方 Github 上進行下載,如果速度較慢,可以嘗試國內的碼云 Gitee 下載,

# 從阿里云下載jar包
curl -O https://arthas.aliyun.com/arthas-boot.jar
# github下載
wget https://alibaba.github.io/arthas/arthas-boot.jar
# 或者 Gitee 下載
wget https://arthas.gitee.io/arthas-boot.jar
# 列印幫助資訊
java -jar arthas-boot.jar -h

3.2 運行

Arthas 只是一個 java 程式,所以可以直接用 java -jar 運行,運行時或者運行之后要選擇要監測的 Java 行程,

# 運行方式1,先運行,在選擇 Java 行程 PID
java -jar arthas-boot.jar
# 選擇行程(輸入[]內編號(不是PID)回車)
eternity@01ak31301447966 project % java -jar arthas-boot.jar 
[INFO] arthas-boot version: 3.4.0
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 769 
  [2]: 58579 org.jeecg.JeecgApplication
  [3]: 58580 org.jetbrains.jps.cmdline.Launcher
  [4]: 27300 org.jetbrains.jps.cmdline.Launcher
  [5]: 32197 myssh.App

# 運行方式2,運行時選擇 Java 行程 PID
java -jar arthas-boot.jar [PID]

查看 PID 的方式可以通過 ps 命令,也可以通過 JDK 提供的 jps命令,

注意:命令視窗要在電腦或服務器環境下,不能進入arthas環境下

# 查看運行的 java 行程資訊
$ jps -mlvV 
# 篩選 java 行程資訊
$ jps -mlvV | grep [xxx]

jps 篩選想要的行程方式,

eternity@01ak31301447966 ~ % jps -mlvV|grep arthas
67250 arthas-boot.jar

在出現 Arthas Logo 之后就可以使用命令進行問題診斷了,下面會詳細介紹,

Arthas 啟動

更多的啟動方式可以參考 help 幫助命令,

# 其他用法
EXAMPLES:
  java -jar arthas-boot.jar <pid>
  java -jar arthas-boot.jar --target-ip 0.0.0.0
  java -jar arthas-boot.jar --telnet-port 9999 --http-port -1
  java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws'
  java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws'
--agent-id bvDOe8XbTM2pQWjF4cfw
  java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'
  java -jar arthas-boot.jar -c 'sysprop; thread' <pid>
  java -jar arthas-boot.jar -f batch.as <pid>
  java -jar arthas-boot.jar --use-version 3.1.4
  java -jar arthas-boot.jar --versions
  java -jar arthas-boot.jar --session-timeout 3600
  java -jar arthas-boot.jar --attach-only
  java -jar arthas-boot.jar --repo-mirror aliyun --use-http

3.3 web console

Arthas 目前支持 Web Console,在成功啟動連接行程之后就已經自動啟動,可以直接訪問 http://127.0.0.1:8563/ 訪問,頁面上的操作模式和控制臺完全一樣,

web-console

3.4 常用命令

下面列舉一些 Arthas 的常用命令,看到這里你可能還不知道怎么使用,別急,后面會一一介紹,

命令 介紹
dashboard 當前系統的實時資料面板
thread 查看當前 JVM 的執行緒堆疊資訊
watch 方法執行資料觀測
trace 方法內部呼叫路徑,并輸出方法路徑上的每個節點上耗時
stack 輸出當前方法被呼叫的呼叫路徑
tt 方法執行資料的時空隧道,記錄下指定方法每次呼叫的入參和回傳資訊,并能對這些不同的時間下呼叫進行觀測
monitor 方法執行監控
jvm 查看當前 JVM 資訊
vmoption 查看,更新 JVM 診斷相關的引數
sc 查看 JVM 已加載的類資訊
sm 查看已加載類的方法資訊
jad 反編譯指定已加載類的原始碼
classloader 查看 classloader 的繼承樹,urls,類加載資訊
heapdump 類似 jmap 命令的 heap dump 功能

3.5 退出

使用 shutdown 退出時 Arthas 同時自動重置所有增強過的類 ,

4、Arthas 常用操作

上面已經了解了什么是 Arthas,以及 Arthas 的啟動方式,下面會依據一些情況,詳細說一說 Arthas 的使用方式,在使用命令的程序中如果有問題,每個命令都可以是 -h 查看幫助資訊,

首先撰寫一個有各種情況的測驗類運行起來,再使用 Arthas 進行問題定位,

import java.util.HashSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * Arthas Demo
 * 公眾號:未讀代碼
 *
 * @Author niujinpeng
 */
@Slf4j
public class Arthas {

    private static HashSet hashSet = new HashSet();
    /** 執行緒池,大小1*/
    private static ExecutorService executorService = Executors.newFixedThreadPool(1);

    public static void main(String[] args) {
        // 模擬 CPU 過高,這里注釋掉了,測驗時可以打開
        // cpu();
        // 模擬執行緒阻塞
        thread();
        // 模擬執行緒死鎖
        deadThread();
        // 不斷的向 hashSet 集合增加資料
        addHashSetThread();
    }

    /**
     * 不斷的向 hashSet 集合添加資料
     */
    public static void addHashSetThread() {
        // 初始化常量
        new Thread(() -> {
            int count = 0;
            while (true) {
                try {
                    hashSet.add("count" + count);
                    Thread.sleep(10000);
                    count++;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static void cpu() {
        cpuHigh();
        cpuNormal();
    }

    /**
     * 極度消耗CPU的執行緒
     */
    private static void cpuHigh() {
        Thread thread = new Thread(() -> {
            while (true) {
                log.info("cpu start 100");
            }
        });
        // 添加到執行緒
        executorService.submit(thread);
    }

    /**
     * 普通消耗CPU的執行緒
     */
    private static void cpuNormal() {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    log.info("cpu start");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

    /**
     * 模擬執行緒阻塞,向已經滿了的執行緒池提交執行緒
     */
    private static void thread() {
        Thread thread = new Thread(() -> {
            while (true) {
                log.debug("thread start");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        // 添加到執行緒
        executorService.submit(thread);
    }

    /**
     * 死鎖
     */
    private static void deadThread() {
        /** 創建資源 */
        Object resourceA = new Object();
        Object resourceB = new Object();
        // 創建執行緒
        Thread threadA = new Thread(() -> {
            synchronized (resourceA) {
                log.info(Thread.currentThread() + " get ResourceA");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info(Thread.currentThread() + "waiting get resourceB");
                synchronized (resourceB) {
                    log.info(Thread.currentThread() + " get resourceB");
                }
            }
        });

        Thread threadB = new Thread(() -> {
            synchronized (resourceB) {
                log.info(Thread.currentThread() + " get ResourceB");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.info(Thread.currentThread() + "waiting get resourceA");
                synchronized (resourceA) {
                    log.info(Thread.currentThread() + " get resourceA");
                }
            }
        });
        threadA.start();
        threadB.start();
    }
}

4.1 全域監控

使用 dashboard 命令可以概覽程式的 執行緒、記憶體、GC、運行環境資訊,

dashboard

4.2 CPU 為什么起飛了

上面的代碼例子有一個 CPU 空轉的死回圈,非常的消耗 CPU性能,那么怎么找出來呢?

使用 thread查看所有執行緒資訊,同時會列出每個執行緒的 CPU 使用率,可以看到圖里 ID 為12 的執行緒 CPU 使用100%,
img

使用命令 thread 12 查看 CPU 消耗較高的 12 號執行緒資訊,可以看到 CPU 使用較高的方法和行數(這里的行數可能和上面代碼里的行數有區別,因為上面的代碼在我寫文章時候重新排過版了),

img

上面是先通過觀察總體的執行緒資訊,然后查看具體的執行緒運行情況,如果只是為了尋找 CPU 使用較高的執行緒,可以直接使用命令 thread -n [顯示的執行緒個數] ,就可以排列出 CPU 使用率 Top N 的執行緒,

img

定位到的 CPU 使用最高的方法,

img

4.3 執行緒池執行緒狀態

定位執行緒問題之前,先回顧一下執行緒的幾種常見狀態:

  • RUNNABLE 運行中
  • TIMED_WAITIN呼叫了以下方法的執行緒會進入TIMED_WAITING
    1. Thread#sleep()
    2. Object#wait() 并加了超時引數
    3. Thread#join() 并加了超時引數
    4. LockSupport#parkNanos()
    5. LockSupport#parkUntil()
  • WAITING當執行緒呼叫以下方法時會進入WAITING狀態:
    1. Object#wait() 而且不加超時引數
    2. Thread#join() 而且不加超時引數
    3. LockSupport#park()
  • BLOCKED 阻塞,等待鎖

上面的模擬代碼里,定義了執行緒池大小為1 的執行緒池,然后在 cpuHigh 方法里提交了一個執行緒,在 thread方法再次提交了一個執行緒,后面的這個執行緒因為執行緒池已滿,會阻塞下來,

使用 thread | grep pool 命令查看執行緒池里執行緒資訊,

img

可以看到執行緒池有 WAITING 的執行緒,

img

4.4 執行緒死鎖

上面的模擬代碼里 deadThread方法實作了一個死鎖,使用 thread -b 命令查看直接定位到死鎖資訊,

/**
 * 死鎖
 */
private static void deadThread() {
    /** 創建資源 */
    Object resourceA = new Object();
    Object resourceB = new Object();
    // 創建執行緒
    Thread threadA = new Thread(() -> {
        synchronized (resourceA) {
            log.info(Thread.currentThread() + " get ResourceA");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info(Thread.currentThread() + "waiting get resourceB");
            synchronized (resourceB) {
                log.info(Thread.currentThread() + " get resourceB");
            }
        }
    });

    Thread threadB = new Thread(() -> {
        synchronized (resourceB) {
            log.info(Thread.currentThread() + " get ResourceB");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info(Thread.currentThread() + "waiting get resourceA");
            synchronized (resourceA) {
                log.info(Thread.currentThread() + " get resourceA");
            }
        }
    });
    threadA.start();
    threadB.start();
}

檢查到的死鎖資訊,

img

注意, 目前只支持找出synchronized關鍵字阻塞住的執行緒, 如果是java.util.concurrent.Lock, 目前還不支持,

4.5 反編譯

上面的代碼放到了包 com下,假設這是一個執行緒環境,當懷疑當前運行的代碼不是自己想要的代碼時,可以直接反編譯出代碼,也可以選擇性的查看類的欄位或方法資訊,

如果懷疑不是自己的代碼,可以使用 jad 命令直接反編譯 class,

# 自己定義的類
[arthas@20451]$ jad com.fridge.service.impl.CardActivateAppServiceImpl

ClassLoader:                                                                    
+-sun.misc.Launcher$AppClassLoader@18b4aac2                                     
  +-sun.misc.Launcher$ExtClassLoader@4278a03f                                   

Location:                                                                       
/Users/admin/project/uhome/Doc_01_Uhome/mythicalanimals/target/classes/         

/*
 * Decompiled with CFR.
 * 
 * Could not load the following classes:
 *  com.fridge.common.base.enumeration.HttpCodeEnum
 *  com.fridge.common.base.exception.CommonException
 *  com.fridge.common.bus.inputVO.CardActivateAppInputVO
 *  com.fridge.common.bus.inputVO.CardActivateEditStatusInputVO
 *  com.fridge.common.bus.inputVO.CardActivateQueryInputVO
 *  com.fridge.common.bus.outVO.CardActivateAppOutputVO
 *  com.fridge.common.bus.outVO.CardActivateQueryOutPutVO
 *  com.fridge.dao.sl.CardAfterRecordMapper
 *  com.fridge.dao.sl.CardConsumeDetailMapper
 *  com.fridge.example.CardAfterRecordExample
 *  com.fridge.example.CardAfterRecordExample$Criteria
 *  com.fridge.model.CardActivate
 *  com.fridge.model.CardDevice
 *  com.fridge.model.CardRules
 *  com.fridge.model.CardStore
 *  com.fridge.model.CardUser
 *  com.fridge.repository.CardActivateAppRepository
 *  com.fridge.repository.CardUserRepository
 *  com.fridge.service.CardActivateAppService
 *  com.fridge.util.BeanUtils
 */
package com.fridge.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class CardActivateAppServiceImpl
implements CardActivateAppService {
    private static final Logger log = LoggerFactory.getLogger(CardActivateAppServiceImpl.class);
    @Autowired
    private CardActivateAppRepository cardActivateAppRepository;
    @Autowired
    private CardUserRepository cardUserRepository;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private CardAfterRecordMapper cardAfterRecordMapper;
    @Autowired
    private CardConsumeDetailMapper cardConsumeDetailMapper;

    public CardActivateAppOutputVO queryCardActivateStatus(CardActivateAppInputVO inputVO) {
        this.checkParams(inputVO);
        CardActivateAppOutputVO outputVO = new CardActivateAppOutputVO();
        String deviceMac = inputVO.getDeviceMac();
        String userId = inputVO.getUserId();
        List devices = this.cardActivateAppRepository.queryCardDeviceByMac(deviceMac);
        List cardActivates = this.cardActivateAppRepository.queryCardActivateByUserId(userId);
        String[] arr = new String[]{"0", "1", "2"};
        List cardActivatesMac = this.cardActivateAppRepository.queryCardActivateByMac(deviceMac, Arrays.asList(arr));
        if (CollUtil.isEmpty(cardActivatesMac)) {
            if (CollUtil.isEmpty(devices)) {
                if (CollUtil.isEmpty(cardActivates)) { 
                    this.getCardActivateOutputVOInvalid(outputVO);
                    return outputVO;
                }
                outputVO.setStatus(Integer.valueOf(2));
                return outputVO;
            }
            outputVO = this.getCardActivateOutputVOvalid(devices, userId, deviceMac);
            return outputVO;
        }
 	 			......

jad 命令還提供了一些其他引數:

# 反編譯只顯示原始碼
jad --source-only com.Arthas
# 反編譯某個類的某個方法
jad --source-only com.Arthas mysql

4.6 查看欄位資訊

使用 **sc -d -f ** 命令查看類的欄位資訊,

[arthas@20252]$ sc -d -f com.Arthas
sc -d -f com.Arthas
 class-info        com.Arthas
 code-source       /C:/Users/Niu/Desktop/arthas/target/classes/
 name              com.Arthas
 isInterface       false
 isAnnotation      false
 isEnum            false
 isAnonymousClass  false
 isArray           false
 isLocalClass      false
 isMemberClass     false
 isPrimitive       false
 isSynthetic       false
 simple-name       Arthas
 modifier          public
 annotation
 interfaces
 super-class       +-java.lang.Object
 class-loader      +-sun.misc.Launcher$AppClassLoader@18b4aac2
                     +-sun.misc.Launcher$ExtClassLoader@2ef1e4fa
 classLoaderHash   18b4aac2
 fields            modifierfinal,private,static
                   type    org.slf4j.Logger
                   name    log
                   value   Logger[com.Arthas]

                   modifierprivate,static
                   type    java.util.HashSet
                   name    hashSet
                   value   [count1, count2]

                   modifierprivate,static
                   type    java.util.concurrent.ExecutorService
                   name    executorService
                   value   java.util.concurrent.ThreadPoolExecutor@71c03156[Ru
                           nning, pool size = 1, active threads = 1, queued ta
                           sks = 0, completed tasks = 0]


Affect(row-cnt:1) cost in 9 ms.


# 解釋
class-info        com.fridge.service.impl.CardActivateAppServiceImpl           
 code-source       /Users/admin/project/uhome/Doc_01_Uhome/mythicalanimals/targ 
                   et/classes/                                                  
 name              com.fridge.service.impl.CardActivateAppServiceImpl           
 # 是否是介面
 isInterface       false                                                        
 # 是否是注解
 isAnnotation      false                                                        
 # 是否是列舉
 isEnum            false                                                        
 # 是否是匿名類
 isAnonymousClass  false                                                        
 # 是否是陣列
 isArray           false                                                        
 isLocalClass      false                                                        
 isMemberClass     false                                                        
 isPrimitive       false                                                        
 isSynthetic       false                                                        
 # 類名
 simple-name       CardActivateAppServiceImpl                                   
 # 修飾符
 modifier          public    
 # 使用的注解
 annotation        org.springframework.stereotype.Service                       
 # 實作的介面
 interfaces        com.fridge.service.CardActivateAppService                    
 # 父類
 super-class       +-java.lang.Object                                           
 # 類加載器
 class-loader      +-sun.misc.Launcher$AppClassLoader@18b4aac2                  
                     +-sun.misc.Launcher$ExtClassLoader@4278a03f

4.7 查看方法資訊

使用 sm 命令查看類的方法資訊,

[arthas@22180]$ sm com.Arthas
com.Arthas <init>()V
com.Arthas start()V
com.Arthas thread()V
com.Arthas deadThread()V
com.Arthas lambda$cpuHigh$1()V
com.Arthas cpuHigh()V
com.Arthas lambda$thread$3()V
com.Arthas addHashSetThread()V
com.Arthas cpuNormal()V
com.Arthas cpu()V
com.Arthas lambda$addHashSetThread$0()V
com.Arthas lambda$deadThread$4(Ljava/lang/Object;Ljava/lang/Object;)V
com.Arthas lambda$deadThread$5(Ljava/lang/Object;Ljava/lang/Object;)V
com.Arthas lambda$cpuNormal$2()V
Affect(row-cnt:16) cost in 6 ms.

4.8 對變數的值很是好奇

使用 ognl 命令,ognl 運算式可以輕松操作想要的資訊,

代碼還是上面的示例代碼,我們查看變數 hashSet 中的資料:

img

查看靜態變數 hashSet 資訊,

[arthas@19856]$ ognl '@com.Arthas@hashSet'
@HashSet[
    @String[count1],
    @String[count2],
    @String[count29],
    @String[count28],
    @String[count0],
    @String[count27],
    @String[count5],
    @String[count26],
    @String[count6],
    @String[count25],
    @String[count3],
    @String[count24],

查看靜態變數 hashSet 大小,

[arthas@19856]$ ognl '@[email protected]()'
	@Integer[57]

甚至可以進行操作,

[arthas@19856]$ ognl  '@[email protected]("test")'
	@Boolean[true]
[arthas@19856]$
# 查看添加的字符
[arthas@19856]$ ognl  '@com.Arthas@hashSet' | grep test
    @String[test],
[arthas@19856]$

ognl 可以做很多事情,可以參考 ognl 運算式特殊用法( https://github.com/alibaba/arthas/issues/71 ),

4.9 程式有沒有問題

4.9.1 運行較慢、耗時較長

使用 trace 命令可以跟蹤統計方法耗時

這次換一個模擬代碼,一個最基礎的 Springboot 專案(當然,不想 Springboot 的話,你也可以直接在 UserController 里 main 方法啟動)控制層 getUser 方法呼叫了 userService.get(uid);,這個方法中分別進行checkserviceredismysql操作,

@RestController
@Slf4j
public class UserController {

    @Autowired
    private UserServiceImpl userService;

    @GetMapping(value = https://www.cnblogs.com/eternityz/p/"/user")
    public HashMap getUser(Integer uid) throws Exception {
        // 模擬用戶查詢
        userService.get(uid);
        HashMap hashMap = new HashMap<>();
        hashMap.put("uid", uid);
        hashMap.put("name", "name" + uid);
        return hashMap;
    }
}

模擬代碼 Service:

@Service
@Slf4j
public class UserServiceImpl {

    public void get(Integer uid) throws Exception {
        check(uid);
        service(uid);
        redis(uid);
        mysql(uid);
    }

    public void service(Integer uid) throws Exception {
        int count = 0;
        for (int i = 0; i < 10; i++) {
            count += i;
        }
        log.info("service  end {}", count);
    }

    public void redis(Integer uid) throws Exception {
        int count = 0;
        for (int i = 0; i < 10000; i++) {
            count += i;
        }
        log.info("redis  end {}", count);
    }

    public void mysql(Integer uid) throws Exception {
        long count = 0;
        for (int i = 0; i < 10000000; i++) {
            count += i;
        }
        log.info("mysql end {}", count);
    }

 	 public boolean check(Integer uid) throws Exception {
         if (uid == null || uid < 0) {
             log.error("uid不正確,uid:{}", uid);
             throw new Exception("uid不正確");
         }
         return true;
     }
}

運行 Springboot 之后,使用 **trace== ** 命令開始檢測耗時情況,

[arthas@6592]$ trace com.UserController getUser

訪問介面 /getUser ,可以看到耗時資訊,看到 com.UserServiceImpl:get()方法耗時較高,
img

繼續跟蹤耗時高的方法,然后再次訪問,

[arthas@6592]$ trace com.UserServiceImpl get

img

很清楚的看到是 com.UserServiceImplmysql方法耗時是最高的,

Affect(class-cnt:1 , method-cnt:1) cost in 31 ms.
`---ts=2019-10-16 14:40:10;thread_name=http-nio-8080-exec-8;id=1f;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@23a918c7
    `---[6.792201ms] com.UserServiceImpl:get()
        +---[0.008ms] com.UserServiceImpl:check() #17
        +---[0.076ms] com.UserServiceImpl:service() #18
        +---[0.1089ms] com.UserServiceImpl:redis() #19
        `---[6.528899ms] com.UserServiceImpl:mysql() #20

4.9.2 統計方法耗時

使用 monitor 命令監控統計方法的執行情況,

每5秒統計一次 com.UserServiceImpl 類的 get 方法執行情況,

monitor -c 5 com.UserServiceImpl get
1

img

4.10 想觀察方法資訊

下面的示例用到了文章的前兩個模擬代碼,

4.10.1 觀察方法的入參出參資訊

使用 watch 命令輕松查看輸入輸出引數以及例外等資訊,

 USAGE:
   watch [-b] [-e] [-x <value>] [-f] [-h] [-n <value>] [-E] [-M <value>] [-s] class-pattern method-pattern express [condition-express]

 SUMMARY:
   Display the input/output parameter, return object, and thrown exception of specified method invocation
   The express may be one of the following expression (evaluated dynamically):
           target : the object
            clazz : the object's class
           method : the constructor or method
           params : the parameters array of method
     params[0..n] : the element of parameters array
        returnObj : the returned object of method
         throwExp : the throw exception of method
         isReturn : the method ended by return
          isThrow : the method ended by throwing exception
            #cost : the execution time in ms of method invocation
 Examples:
   watch -b org.apache.commons.lang.StringUtils isBlank params
   watch -f org.apache.commons.lang.StringUtils isBlank returnObj
   watch org.apache.commons.lang.StringUtils isBlank '{params, target, returnObj}' -x 2
   watch -bf *StringUtils isBlank params
   watch *StringUtils isBlank params[0]
   watch *StringUtils isBlank params[0] params[0].length==1
   watch *StringUtils isBlank params '#cost>100'
   watch -E -b org\.apache\.commons\.lang\.StringUtils isBlank params[0]

 WIKI:
   https://alibaba.github.io/arthas/watch

常用操作:

# 查看入參和出參
$ watch com.Arthas addHashSet '{params[0],returnObj}'
# 查看入參和出參大小
$ watch com.Arthas addHashSet '{params[0],returnObj.size}'
# 查看入參和出參中是否包含 'count10'
$ watch com.Arthas addHashSet '{params[0],returnObj.contains("count10")}'
# 查看入參和出參,出參 toString
$ watch com.Arthas addHashSet '{params[0],returnObj.toString()}'
12345678

查看入參出參,

img

查看回傳的例外資訊,

4.10.2 觀察方法的呼叫路徑

使用 stack命令查看方法的呼叫資訊,

# 觀察 類com.UserServiceImpl的 mysql 方法呼叫路徑
stack com.UserServiceImpl mysql
12

img

4.10.3 方法呼叫時空隧道

使用 tt 命令記錄方法執行的詳細情況,

tt 命令方法執行資料的時空隧道,記錄下指定方法每次呼叫的入參和回傳資訊,并能對這些不同的時間下呼叫進行觀測 ,

常用操作:

開始記錄方法呼叫資訊:tt -t com.UserServiceImpl check

img

可以看到記錄中 INDEX=1001 的記錄的 IS-EXP = true ,說明這次呼叫出現例外,

查看記錄的方法呼叫資訊: tt -l

img

查看呼叫記錄的詳細資訊(-i 指定 INDEX): tt -i 1001

img

可以看到 INDEX=1001 的記錄的例外資訊,

重新發起呼叫,使用指定記錄,使用 -p 重新呼叫,

tt -i 1001 -p

img

文中代碼已經上傳到 Github,

https://github.com/niumoo/lab-notes/tree/master/src/main/java/net/codingme/arthas

站在巨人肩膀上摘蘋果

https://blog.csdn.net/u013735734/article/details/102930307

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/5033.html

標籤:Java

上一篇:常用十大演算法(二)— 分治演算法

下一篇:自帶火焰圖的Java性能分析工具Async-profiler

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more