【死磕JVM】用Arthas排查JVM內存 真爽!我從小用到大

在這裏插入圖片描述

Arthas是啥

當咱們系統遇到JVM或者內存溢出等問題的時候,如何對咱們的程序進行有效的監控和排查,就發現了幾個比較經常使用的工具,好比JDK自帶的 jconsole、jvisualvm還有一個最好用的工具——jprofiler,可是這個是收費的,或者除了頗有錢的公司,通常不多人會用這個,還有一個就是咱們今天的主角——Arthas ,爲何今天會重點講這個呢?html

官網地址:arthas.gitee.io/ GitHub地址:github.com/alibaba/art…java

Arthas 是Alibaba開源的Java診斷工具,採用命令行交互模式,提供了較爲豐富的功能,主要仍是他是免費裏面的算是好用且功能比較強大的一個JVM排查的插件,在瞭解這個利器以後,發現仍是挺好用的,並且支持的功能也比較全面,那麼Arthas到底能夠爲咱們作哪些事情呢?git

  1. 提供性能看板,包括線程、cpu、內存等信息,而且會定時的刷新。
  2. 根據各類條件查看線程快照。找出cpu佔用率最高的n個線程
  3. 輸出jvm的各類信息,如gc算法、jdk版本、ClassPath等
  4. 遇到問題沒法在線上 debug,熱部署加日誌直接替換
  5. 查看某個類的靜態屬性,也能夠經過ognl語法執行一些語句
  6. 查看已加載的類的詳細信息,這個類從哪一個jar包加載的,查看類的方法的信息
  7. dump 類的字節碼到指定目錄
  8. 直接反編譯指定的類
  9. 快速定位應用的熱點,生成火焰圖
  10. 能夠監控到JVM的實時運行狀態

之前,你碰到這些問題,解決的辦法大可能是,修改代碼,從新上線。可是在大公司裏,上線的流程是很是繁瑣的,若是爲了多加一行日誌而從新發布版本,無疑是很是折騰人的。可是阿里巴巴開源的Arthas 有了更爲優雅的線上調試方法。github

Arthas 支持JDK6,同時能夠在 Linux/Mac/Windows上運行,自動Tab 補全功能,更方便咱們定位問題和診斷web

下載地址:arthas.gitee.io/download.ht… 你能夠下載zip的包我下載的是arthas-packaging-3.5.0-bin.zip 或者經過命令去下載正則表達式

wget https://alibaba.github.io/arthas/arthas-boot.jar算法

使用手冊

1. 快速啓動

當咱們下載好以後,咱們直接經過命令啓動就能夠java -jar arthas-boot.jar,可是在此以前咱們須要經過檢測的代碼來掛靠到Arthas上面apache

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class FullGCTest {


    //模擬銀行卡的類
    private static class CardInfo {
        //小農的銀行卡信息記錄
        BigDecimal price = new BigDecimal(10000000.0);
        String name = "牧小農";
        int age = 18;
        Date birthdate = new Date();

        public void m() {}
    }

    //線程池 定時線程池
    //50個,而後設置 拒絕策略
    private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
            new ThreadPoolExecutor.DiscardOldestPolicy());

    public static void main(String[] args) throws Exception {
        executor.setMaximumPoolSize(50);

        for (;;){
            modelFit();
            Thread.sleep(100);
        }
    }

    /** * 對銀行卡進行風險評估 */
    private static void modelFit(){
        List<CardInfo> taskList = getAllCardInfo();
        //拿出每個信息出來
        taskList.forEach(info -> {
            // do something
            executor.scheduleWithFixedDelay(() -> {
                //調用M方法
                info.m();

            }, 2, 3, TimeUnit.SECONDS);
        });
    }

    private static List<CardInfo> getAllCardInfo(){
        List<CardInfo> taskList = new ArrayList<>();
        //每次查詢100張卡出來
        for (int i = 0; i < 100; i++) {
            CardInfo ci = new CardInfo();
            taskList.add(ci);
        }

        return taskList;
    }
}

複製代碼

這個是上篇文章講述的案例,感興趣的能夠了解一下。centos

首先咱們須要使用javac 命令將Java文件進行編譯javac FullGCTest.java進行編譯,而後打印GC日誌,進行風險監控打印GC日誌: java -Xms200M -Xmx200M -XX:+PrintGC FullGCTest服務器

Arthas啓動命令:java -jar arthas-boot.jar

在這裏插入圖片描述

咱們就看到了咱們剛纔啓動的FullGCTest的應用程序,咱們輸入編號 1 回車,這樣咱們就把Arthas掛靠到咱們的程序上,接下來咱們只須要作對應的命令操做就能夠了 在這裏插入圖片描述

命令詳情文檔:arthas.aliyun.com/doc/command…

2. 功能列表

命令 詳細說明
jvm 查看當前JVM信息
thread 查看當前JVM的線程堆棧信息
watch 方法執行數據觀測
dashboard 當前系統的實時數據面板
trace 方法內部調用路徑,並輸出方法路徑上的每一個節點上耗時
stack 輸出當前方法被調用的調用路徑
tt 方法執行數據的時空隧道,記錄下指定方法每次調用的入參和返回信息,並能對這些不一樣的時間下調用進行觀測
vmoption 查看,更新JVM已加載的類信息
sc 查看JVM已加載的類信息
sm 查看已加載類的方法信息
jad 反編譯指定已加載類的源碼
classloader 查看classloader的繼承樹,urls,類加載信息
heapdump 相似jmap命令的heap dump 功能

jvm

在這裏插入圖片描述

OPERATING-SYSTEM:系統相關參數

THREAD相關:

  • COUNT : JVM當前活躍的線程數
  • DAEMON-COUNT : JVM當前活躍的守護線程數
  • PEAK-COUNT: 從JVM啓動開始曾經活着的最大線程數
  • STARTED-COUNT: 從JVM啓動開始總共啓動過的線程次數
  • DEADLOCK-COUNT: JVM當前死鎖的線程數

MEMORY

FILE-DESCRIPTOR(文件描述符相關):

  • MAX-FILE-DESCRIPTOR-COUNT:JVM進程最大能夠打開的文件描述符數
  • OPEN-FILE-DESCRIPTOR-COUNT:JVM當前打開的文件描述符數

thread 命令

參數說明:

命令 詳細說明
id 線程id
[n:] 指定最忙的前N個線程並打印堆棧
[b] 找出當前阻塞其餘線程的線程
[i] 指定cpu使用率統計的採樣間隔,單位爲毫秒,默認值爲200
[--all] 顯示全部匹配的線程

打印當前最忙的N個線程並打印堆棧 thread -n 3

在這裏插入圖片描述

thread 查看全部線程 在這裏插入圖片描述

thread 17: 顯示指定線程的運行堆棧 在這裏插入圖片描述

thread -i: 指定採樣時間間隔

thread -i 1000 : 統計最近1000ms內的線程CPU時間。 thread -n 3 -i 1000 : 列出1000ms內最忙的3個線程棧

dashboard 命令

運行程序時,會顯示當前程序的實時信息,如qps, rt, 錯誤數, 線程池信息等等

在這裏插入圖片描述

數聽說明:

  • ID: Java級別的線程ID
  • NAME: 線程名
  • GROUP: 線程組名
  • PRIORITY: 線程優先級, 1~10之間的數字,越大表示優先級越高
  • STATE: 線程的狀態CPU%: 線程的cpu使用率。好比採樣間隔1000ms,某個線程的增量cpu時間爲100ms,則cpu使用率=100/1000=10%
  • DELTA_TIME: 上次採樣以後線程運行增量CPU時間,數據格式爲秒
  • TIME: 線程運行總CPU時間,數據格式爲分:秒
  • INTERRUPTED: 線程當前的中斷位狀態
  • DAEMON: 是不是daemon線程

在這裏插入圖片描述

參數說明:

參數名稱 詳細說明
id 刷新實時數據的時間間隔 (ms),默認5000ms
[n:] 刷新實時數據的次數

sc 命令

查看JVM已加載的類信息,經過SC咱們能夠看到咱們這個類的詳細信息,包括是從哪一個jar包讀取的,他是否是接口/枚舉類等,甚至包括他是從哪一個類加載器加載的。

參數說明:

參數名稱 詳細說明
class-pattern 類名錶達式匹配
method-pattern 方法名錶達式匹配
[d] 輸出當前類的詳細信息,包括這個類所加載的原始文件來源、類的聲明、加載的ClassLoader等詳細信息。若是一個類被多個ClassLoader所加載,則會出現屢次
[E] 開啓正則表達式匹配,默認爲通配符匹配

sc -d *CardInfo: 打印類的詳細信息 在這裏插入圖片描述

sc -d -f *CardInfo:打印類的Fiedld信息

在這裏插入圖片描述

heapdump + jhat分析

heapdump:相似於jmap命令

建立到指定文件夾下:

[arthas@365564]$ heapdump /usr/local/mxn/dump.hprof
Dumping heap to /usr/local/mxn/dump.hprof ...
Heap dump file created
複製代碼

建立成功後,咱們就能夠在指定文件夾下看到對應的dump文件,而後使用命令jhat dump.hprof,生成文件,成功後咱們就能夠經過IP+端口進行訪問了 在這裏插入圖片描述

訪問: 在這裏插入圖片描述

而後咱們就能夠經過IP+端口去訪問它了,裏面有個他的other,咱們拉到最底下,找 Show instance counts for all classes (including platform) 在這裏插入圖片描述

從下面咱們能夠分析出來哪一個類包含的對象最多,分析出來哪一個類產生的對象

在這裏插入圖片描述

這個裏面最強大的功能仍是叫作 Execute Object Query Language (OQL) query,這個裏面能夠顯示有哪些對象,對象有多少個字節和引用,能夠觀察到哪一個對象產生了問題,以下圖所示,顯示全部String對應的對象 在這裏插入圖片描述 在這裏插入圖片描述

搜索點進去以後咱們還能看到這個對象到底佔用了多少個字節,有多少個引用指向了這個Object,這個OQL的語法也是很靈活,咱們可使用where條件去過濾

在這裏插入圖片描述

jad

jad:反編譯某個類,或者反編譯某個類的某個方法,動態代理生成類的問題定位 第三方的類(觀察代碼) 版本問題(肯定本身最新提交的版本是否是被使用)

在這裏插入圖片描述

有人可能會問這個有啥用,源碼我不是本身就知道嗎?由於有時咱們常常會不肯定線上或者測試環境的包是不是咱們修改過的,這時候就能夠經過jad反編譯來看下,是不是最新的代碼

redafine

redafine:熱替換,動態更新代碼,不用重啓jvm目前有些限制條件:只能改方法實現(方法已經運行完成),不能改方法名, 不能改屬性 m() -> mm()

好比咱們在線上環境有個class確認有問題,想要從新替換,通常狀況下只能停掉服務器從新發布,在普通的小公司這樣是能夠的,可是在大規模公司京東淘寶這樣的是不能停的,由於整個流程是很是複雜的,那怎麼辦呢?你們能夠看到下面的案例

首先咱們新建一個測試案例:

public class T{
    public static void main(String[] args) throws Exception{
                    for(;;){
                    System.in.read();
                    new TT().m();
                }
        }
}
複製代碼
public class TT{
        public void m(){
        System.out.println(2);
    }
}
複製代碼

使用命令javac *.java,編譯成class文件,而後運行 T 文件

[root@VM-0-7-centos t]# java T
a
2
2
複製代碼

當咱們輸入a的時候打印2,可是咱們上線之後才發現,咱們須要輸出的1,這個是若是要從本地更改要從新發布上線,爲了這一個修改,明顯是不值當的,可是若是咱們用 redafine 熱部署就能夠幫助咱們直接替換,不用從新發布jvm

而後咱們將 T 這個程序掛靠到 Arthas 上面去 在這裏插入圖片描述

而後咱們直接修改 TT.java 程序 vi TT.java,將裏面打印2的值修改爲1

public class TT{
        public void m(){
            System.out.println(1);
        }
}
複製代碼

而後編譯執行 ```javac TT.java ````

在回到咱們掛靠的Arthas 上面執行 redefine /usr/local/mxn/fuccGc/t/TT.class

[arthas@398842]$ redefine /usr/local/mxn/fuccGc/t/TT.class
redefine success, size: 1, classes:
TT
複製代碼

執行成功 你們能夠看到咱們在沒有從新啓動的狀況下成功替換了class文件 在這裏插入圖片描述

watch

watch:方法執行的數據觀測,能夠經過watch指令,來監控某個類,監控後,運行下你的功能,復現下場景,arthas會提供給你具體的出參和入參,幫助你排查故障

trace

輸出方法調用路徑,並輸出耗時,這個指令對於優化代碼很是的有用,能夠看出具體每一個方法執行的時間,若是是for循環等重複語句,還能看出n次循環中的最大耗時,最小耗時,和平均耗時,完美!

tt

在咱們對某個方法開啓tt後,會記錄每一次調用(咱們能夠設置最大監控次數)的入參和返回參數,並能對這些不一樣時間下調進行觀測

[arthas@405136]$ tt -t FullGCTest modelFit

在這裏插入圖片描述

命令參數解析-t tt 命令有不少個主參數,-t 就是其中之一。這個參數的代表但願記錄下類 *Test 的 print 方法的每次執行狀況。 -n 3 當你執行一個調用量不高的方法時可能你還能有足夠的時間用 CTRL+C 中斷 tt 命令記錄的過程,但若是遇到調用量很是大的方法,瞬間就能將你的 JVM 內存撐爆。

此時你能夠經過 -n 參數指定你須要記錄的次數,當達到記錄次數時 Arthas 會主動中斷tt命令的記錄過程,避免人工操做沒法中止的狀況。

ognl表達式

ognl表達式

OGNL特殊用法請參考:github.com/alibaba/art… OGNL表達式官方指南:commons.apache.org/proper/comm…

調用靜態函數:ognl '@java.lang.System@out.println("hello")' 獲取靜態類的靜態字段:ognl '@FullGCTest@random'

Arthas還支持Web Console,詳見:alibaba.github.io/arthas/web-…

總結

Arthas是一個線上Debug神器,相比於其餘工具,Arthas有着比較全面的功能,上手也比較容易,對於剛開始入門的小夥伴也是能夠輕鬆掌握的,對於文中有不懂或者有問題的小夥伴,你們能夠在下面留言評論。

原創不易,但願你們多多捧場,記得一鍵三連!!!

我是牧小農,怕什麼真理無窮,進一步有進一步的歡喜,你們加油!

相關文章
相關標籤/搜索