天天看點

Java 線上診斷工具 ArthasArthas(阿爾薩斯)

Java 線上診斷工具 ArthasArthas(阿爾薩斯)

Arthas(阿爾薩斯)

工欲善其事 必先利其器 Arthas是Java線上診斷的神器

工具背景

Arthas 是Alibaba開源的Java線上診斷工具,支援JDK 6+,支援Linux/Mac/Winodws,采用指令行互動模式,同時提供豐富的 Tab 自動補全功能,進一步友善進行問題的定位和診斷。

想吐槽一下這個名字,作者是有多愛WOW...

官方文檔:

https://alibaba.github.io/arthas/index.html

應用場景

當你遇到以下類似問題而束手無策時,Arthas可以幫助你解決:

  • 這個類從哪個 jar 包加載的?為什麼會報各種類相關的 Exception?
  • 我改的代碼為什麼沒有執行到?難道是我沒 commit?分支搞錯了?
  • 遇到問題無法線上上 debug,難道隻能通過加日志再重新釋出嗎?
  • 線上遇到某個使用者的資料處理有問題,但線上同樣無法 debug,線下無法重制!
  • 是否有一個全局視角來檢視系統的運作狀況?
  • 有什麼辦法可以監控到JVM的實時運作狀态?

快速安裝

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

快速開始

選擇應用java程序:

$ docker exec -it arthas-demo /bin/sh -c "java -jar /opt/arthas/arthas-boot.jar"
[INFO] arthas-boot version: 3.1.1
[INFO] Found existing java process, please choose one and hit RETURN.
* [1]: 6 /opt/arthas/arthas-demo.jar           

輸入1,再輸入回車/enter。Arthas會attach到目标程序上,并輸出日志:

[INFO] arthas home: /opt/arthas
[INFO] Try to attach process 6
[INFO] Attach process 6 success.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. m,--------.,--.  ,--.  ,---.   ,---.
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'


wiki      https://alibaba.github.io/arthas
tutorials https://alibaba.github.io/arthas/arthas-tutorials
version   3.1.1
pid       6
time      2019-06-12 02:07:35

$           

常用指令

Arthas的具備豐富的功能,并且支援Tab 自動補全,在此不一一列舉,詳見:

https://alibaba.github.io/arthas/advanced-use.html

dashboard

檢視目前系統的實時資料面闆(Thread/Memory/GC/Runtime/Tomcat),按 ctrl+c 退出。

ID             NAME                                        GROUP                         PRIORITY       STATE          %CPU           TIME          INTERRUPTED    DAEMON
46             Timer-for-arthas-dashboard-0417c82e-1367-4c system                        10             RUNNABLE       100            0:0           false          true
10             AsyncAppender-Worker-arthas-cache.result.As system                        9              WAITING        0              0:0           false          true
8              Attach Listener                             system                        9              RUNNABLE       0              0:0           false          true
3              Finalizer                                   system                        8              WAITING        0              0:0           false          true
2              Reference Handler                           system                        10             WAITING        0              0:0           false          true
4              Signal Dispatcher                           system                        9              RUNNABLE       0              0:0           false          true
44             as-command-execute-daemon                   system                        10             TIMED_WAITING  0              0:0           false          true
12             job-timeout                                 system                        9              TIMED_WAITING  0              0:0           false          true
1              main                                        main                          5              TIMED_WAITING  0              0:45          false          false
13             nioEventLoopGroup-2-1                       system                        10             RUNNABLE       0              0:8           false          false
17             nioEventLoopGroup-2-2                       system                        10             RUNNABLE       0              0:8           false          false
33             nioEventLoopGroup-2-3                       system                        10             RUNNABLE       0              0:8           false          false
37             nioEventLoopGroup-2-4                       system                        10             RUNNABLE       0              0:7           false          false
14             nioEventLoopGroup-3-1                       system                        10             RUNNABLE       0              0:8           false          false
15             pool-1-thread-1                             system                        5              TIMED_WAITING  0              0:0           false          false
16             pool-2-thread-1                             system                        5              WAITING        0              0:0           false          false


Memory                                used         total        max         usage        GC
heap                                  56M          106M         441M        12.87%       gc.ps_scavenge.count                         23
ps_eden_space                         30M          65M          143M        21.67%       gc.ps_scavenge.time(ms)                      344
ps_survivor_space                     544K         1024K        1024K       53.13%       gc.ps_marksweep.count                        3
ps_old_gen                            25M          39M          331M        7.62%        gc.ps_marksweep.time(ms)                     215
nonheap                               32M          33M          -1          97.19%
code_cache                            7M           8M           240M        3.28%
metaspace                             22M          22M          -1          97.68%
compressed_class_space                2M           2M           1024M       0.24%
direct                                0K           0K           -           Infinity%
mapped                                0K           0K           -           NaN%


Runtime
os.name                                                                                  Linux
os.version                                                                               4.9.125-linuxkit
java.version                                                                             1.8.0_212
java.home                                                                                /usr/lib/jvm/java-1.8-openjdk/jre
systemload.average                                                                       0.11
processors                                                                               2
uptime                                                                                   185745s
           

thread

檢視目前線程資訊,檢視線程的堆棧。

參數名稱 參數說明
id 線程id
[n:] 指定最忙的前N個線程并列印堆棧
[b] 找出目前阻塞其他線程的線程
[i ] 指定cpu占比統計的采樣間隔,機關為毫秒

顯示指定線程的運作堆棧

$ thread 1
"main" Id=1 TIMED_WAITING
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    at demo.MathGame.main(MathGame.java:17)           

目前最忙的前N個線程并列印堆棧

$ thread -n 3
"as-command-execute-daemon" Id=48 cpuUsage=68% RUNNABLE
    at sun.management.ThreadImpl.dumpThreads0(Native Method)
    at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:448)
    at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:133)
    at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:79)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
    at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
    at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:370)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

    Number of locked synchronizers = 1
    - java.util.concurrent.ThreadPoolExecutor$Worker@503ca440


"nioEventLoopGroup-2-3" Id=33 cpuUsage=31% RUNNABLE (in native)
    at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
    at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
    at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
    -  locked io.netty.channel.nio.SelectedSelectionKeySet@46d33dbd
    -  locked java.util.Collections$UnmodifiableSet@1672a7b2
    -  locked sun.nio.ch.EPollSelectorImpl@17c134d0
    at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
    at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62)
    at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:791)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:439)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:906)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:748)


"Reference Handler" Id=2 cpuUsage=0% WAITING on java.lang.ref.Reference$Lock@138fcfa0
    at java.lang.Object.wait(Native Method)
    -  waiting on java.lang.ref.Reference$Lock@138fcfa0
    at java.lang.Object.wait(Object.java:502)
    at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)           

目前阻塞其他線程的線程

$ thread -b
"http-bio-8080-exec-4" Id=27 TIMED_WAITING
    at java.lang.Thread.sleep(Native Method)
    at test.arthas.TestThreadBlocking.doGet(TestThreadBlocking.java:22)
    -  locked java.lang.Object@725be470 <---- but blocks 4 other threads!
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:624)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at test.filter.TestDurexFilter.doFilter(TestDurexFilter.java:46)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
    at com.taobao.tomcat.valves.ContextLoadFilterValve$FilterChainAdapter.doFilter(ContextLoadFilterValve.java:191)
    at com.taobao.eagleeye.EagleEyeFilter.doFilter(EagleEyeFilter.java:81)
    at com.taobao.tomcat.valves.ContextLoadFilterValve.invoke(ContextLoadFilterValve.java:150)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:429)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1085)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
    -  locked org.apache.tomcat.util.net.SocketWrapper@7127ee12
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
 
    Number of locked synchronizers = 1
    - java.util.concurrent.ThreadPoolExecutor$Worker@31a6493e           

jvm

檢視目前JVM資訊。

$ jvm
 RUNTIME
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 MACHINE-NAME                                       6@4425d962807f
 JVM-START-TIME                                     2019-06-12 02:07:19
 MANAGEMENT-SPEC-VERSION                            1.2
 SPEC-NAME                                          Java Virtual Machine Specification
 SPEC-VENDOR                                        Oracle Corporation
 SPEC-VERSION                                       1.8
 VM-NAME                                            OpenJDK 64-Bit Server VM
 VM-VENDOR                                          IcedTea
 VM-VERSION                                         25.212-b04
 INPUT-ARGUMENTS                                    []
 CLASS-PATH                                         /opt/arthas/arthas-demo.jar
 BOOT-CLASS-PATH                                    /usr/lib/jvm/java-1.8-openjdk/jre/lib/resources.jar:/usr/lib/jvm/java-1.8-openjdk/jre/lib/rt.jar:/usr/lib/jvm/java-1.8-openjd
                                                    k/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-1.8-openjdk/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8-openjdk/jre/lib/jce.jar:/usr/li
                                                    b/jvm/java-1.8-openjdk/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8-openjdk/jre/lib/jfr.jar:/usr/lib/jvm/java-1.8-openjdk/jre/c
                                                    lasses
 LIBRARY-PATH                                       /usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64/server:/usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64:/usr/lib/jvm/java-1.8-openjdk/
                                                    jre/../lib/amd64:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 CLASS-LOADING
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
 LOADED-CLASS-COUNT                                 3653
 TOTAL-LOADED-CLASS-COUNT                           3653
 UNLOADED-CLASS-COUNT                               0
 IS-VERBOSE                                         false

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 COMPILATION
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
 NAME                                               HotSpot 64-Bit Tiered Compilers
 TOTAL-COMPILE-TIME                                 11209(ms)

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 GARBAGE-COLLECTORS
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
 PS Scavenge                                        24/347(ms)
 [count/time]
 PS MarkSweep                                       3/215(ms)
 [count/time]

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 MEMORY-MANAGERS
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
 CodeCacheManager                                   Code Cache

 Metaspace Manager                                  Metaspace
                                                    Compressed Class Space

 PS Scavenge                                        PS Eden Space
                                                    PS Survivor Space

 PS MarkSweep                                       PS Eden Space
                                                    PS Survivor Space
                                                    PS Old Gen


----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 MEMORY
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
 HEAP-MEMORY-USAGE                                  124780544(119.00 MiB)/33554432(32.00 MiB)/462422016(441.00MiB)/52041680(49.63 MiB)
 [committed/init/max/used]
 NO-HEAP-MEMORY-USAGE                               35520512(33.88 MiB)/2555904(2.44 MiB)/-1(-1 B)/34438440(32.84 MiB)
 [committed/init/max/used]
 PENDING-FINALIZE-COUNT                             0

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 OPERATING-SYSTEM
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 OS                                                 Linux
 ARCH                                               amd64
 PROCESSORS-COUNT                                   2
 LOAD-AVERAGE                                       0.00341796875
 VERSION                                            4.9.125-linuxkit

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 THREAD
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 COUNT                                              15
 DAEMON-COUNT                                       7
 PEAK-COUNT                                         16
 STARTED-COUNT                                      46
 DEADLOCK-COUNT                                     0

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 FILE-DESCRIPTOR
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 MAX-FILE-DESCRIPTOR-COUNT                          1048576
 OPEN-FILE-DESCRIPTOR-COUNT                         51           

sysprop

檢視目前JVM的系統屬性(System Property)

檢視全部系統屬性

$ sysprop
 KEY                                 VALUE
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
 java.runtime.name                   OpenJDK Runtime Environment
 sun.boot.library.path               /usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64
 java.vm.version                     25.212-b04
 java.vm.vendor                      IcedTea
 java.vendor.url                     https://icedtea.classpath.org
 path.separator                      :
 java.vm.name                        OpenJDK 64-Bit Server VM
 file.encoding.pkg                   sun.io
 user.country                        US
 sun.java.launcher                   SUN_STANDARD
 sun.os.patch.level                  unknown
 java.vm.specification.name          Java Virtual Machine Specification
 user.dir                            /
 java.runtime.version                1.8.0_212-b04
 JM.LOG.PATH                         /root/logs
 java.awt.graphicsenv                sun.awt.X11GraphicsEnvironment
 java.endorsed.dirs                  /usr/lib/jvm/java-1.8-openjdk/jre/lib/endorsed
 os.arch                             amd64
 java.io.tmpdir                      /tmp
 line.separator

 java.vm.specification.vendor        Oracle Corporation
 os.name                             Linux
 sun.jnu.encoding                    UTF-8
 java.library.path                   /usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64/server:/usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64:/usr/lib/jvm/java-1.8-openjdk/jre/../lib/amd6
                                     4:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
 sun.nio.ch.bugLevel
 java.specification.name             Java Platform API Specification
 java.class.version                  52.0
 sun.management.compiler             HotSpot 64-Bit Tiered Compilers
 os.version                          4.9.125-linuxkit
 user.home                           /root
 user.timezone                       GMT
 java.awt.printerjob                 sun.print.PSPrinterJob
 file.encoding                       UTF-8
 java.specification.version          1.8
 user.name                           root
 java.class.path                     /opt/arthas/arthas-demo.jar
 java.vm.specification.version       1.8
 sun.arch.data.model                 64
 java.home                           /usr/lib/jvm/java-1.8-openjdk/jre
 sun.java.command                    /opt/arthas/arthas-demo.jar
 java.specification.vendor           Oracle Corporation
 user.language                       en
 awt.toolkit                         sun.awt.X11.XToolkit
 java.vm.info                        mixed mode
 java.version                        1.8.0_212
 java.ext.dirs                       /usr/lib/jvm/java-1.8-openjdk/jre/lib/ext:/usr/java/packages/lib/ext
 sun.boot.class.path                 /usr/lib/jvm/java-1.8-openjdk/jre/lib/resources.jar:/usr/lib/jvm/java-1.8-openjdk/jre/lib/rt.jar:/usr/lib/jvm/java-1.8-openjdk/jre/lib/sunrs
                                     asign.jar:/usr/lib/jvm/java-1.8-openjdk/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8-openjdk/jre/lib/jce.jar:/usr/lib/jvm/java-1.8-openjdk/jre/lib
                                     /charsets.jar:/usr/lib/jvm/java-1.8-openjdk/jre/lib/jfr.jar:/usr/lib/jvm/java-1.8-openjdk/jre/classes
 java.vendor                         IcedTea
 file.separator                      /
 java.vendor.url.bug                 https://icedtea.classpath.org/bugzilla
 sun.cpu.endian                      little
 sun.io.unicode.encoding             UnicodeLittle
 sun.cpu.isalist           

檢視單個系統屬性

$ sysprop java.version
java.version=1.8.0_212           

sysenv

檢視目前JVM的環境屬性(System Environment Variables)

檢視所有環境變量

$ sysenv
 KEY                                 VALUE
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
 PATH                                /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin
 JAVA_HOME                           /usr/lib/jvm/java-1.8-openjdk
 TERM                                xterm
 MIRROR_MAVEN_HOST                   http://maven.aliyun.com/repository/public
 MIRROR_ALPINE_HOST                  mirrors.aliyun.com
 LANG                                C.UTF-8
 ALPINE_HOST                         dl-cdn.alpinelinux.org
 HOSTNAME                            4425d962807f
 JAVA_ALPINE_VERSION                 8.212.04-r0
 LD_LIBRARY_PATH                     /usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64/server:/usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64:/usr/lib/jvm/java-1.8-openjdk/jre/../lib/amd6
                                     4
 PWD                                 /
 MAVEN_HOST                          http://repo1.maven.org/maven2
 JAVA_VERSION                        8u212
 HOME                                /root
 SHLVL                               1           

檢視單個環境變量

$ sysenv HOSTNAME
HOSTNAME=4425d962807f           

monitor

方法執行監控,對比對 class-pattern/method-pattern的類、方法的調用進行監控。

class-pattern 類名表達式比對
method-pattern 方法名表達式比對
[E] 開啟正規表達式比對,預設為通配符比對
[c:] 統計周期,預設值為120秒
$ monitor -c 5 demo.MathGame primeFactors
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 26 ms.
 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2019-06-14 06:17:39  demo.MathGame  primeFactors  5      2        3     0.09        60.00%

 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2019-06-14 06:17:44  demo.MathGame  primeFactors  5      3        2     0.03        40.00%

 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2019-06-14 06:17:49  demo.MathGame  primeFactors  5      2        3     0.17        60.00%

 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2019-06-14 06:17:54  demo.MathGame  primeFactors  5      2        3     0.17        60.00%

 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2019-06-14 06:17:59  demo.MathGame  primeFactors  5      2        3     0.12        60.00%

 timestamp            class          method        total  success  fail  avg-rt(ms)  fail-rate
-----------------------------------------------------------------------------------------------
 2019-06-14 06:18:04  demo.MathGame  primeFactors  5      3        2     0.60        40.00%           
  • timestamp: 時間戳
  • class: Java類
  • method: 方法(構造方法、普通方法)
  • total: 調用次數
  • success: 成功次數
  • fail: 失敗次數
  • rt: 平均RT
  • fail-rate: 失敗率

watch

方法執行資料觀測,讓你能友善的觀察到指定方法的調用情況。能觀察到的範圍為:傳回值、抛出異常、入參,通過編寫 OGNL 表達式進行對應變量的檢視。

express 觀察表達式
condition-express 條件表達式
在方法調用之前觀察
[e] 在方法異常之後觀察
[s] 在方法傳回之後觀察
[f] 在方法結束之後(正常傳回和異常傳回)觀察,預設打開
[x:] 指定輸出結果的屬性周遊深度,預設為 1

觀察方法出參和傳回值

$ watch demo.MathGame primeFactors "{params,returnObj}" -x 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 44 ms.
ts=2019-06-14 06:34:19; [cost=0.3721ms] result=@ArrayList[
    @Object[][
        @Integer[15013],
    ],
    @ArrayList[
        @Integer[15013],
    ],
]
ts=2019-06-14 06:34:20; [cost=0.1084ms] result=@ArrayList[
    @Object[][
        @Integer[-75392],
    ],
    null,
]           

觀察方法入參

$ watch demo.MathGame primeFactors "{params,returnObj}" -x 2 -b
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 52 ms.
ts=2019-06-14 06:35:07; [cost=0.0194ms] result=@ArrayList[
    @Object[][
        @Integer[31370],
    ],
    null,
]
ts=2019-06-14 06:35:08; [cost=0.0101ms] result=@ArrayList[
    @Object[][
        @Integer[-82029],
    ],
    null,
]           

同時觀察方法調用前和方法傳回後

$ watch demo.MathGame primeFactors "{params,target,returnObj}" -x 2 -b -s -n 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 40 ms.
ts=2019-06-14 06:35:57; [cost=0.0334ms] result=@ArrayList[
    @Object[][
        @Integer[-136522],
    ],
    @MathGame[
        random=@Random[java.util.Random@533ddba],
        illegalArgumentCount=@Integer[94006],
    ],
    null,
]
ts=2019-06-14 06:35:58; [cost=0.0098ms] result=@ArrayList[
    @Object[][
        @Integer[-16431],
    ],
    @MathGame[
        random=@Random[java.util.Random@533ddba],
        illegalArgumentCount=@Integer[94007],
    ],
    null,
]           

調整-x的值,觀察具體的方法參數值

$ watch demo.MathGame primeFactors "{params,target}" -x 3
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 42 ms.
ts=2019-06-14 06:37:02; [cost=0.085ms] result=@ArrayList[
    @Object[][
        @Integer[151500],
    ],
    @MathGame[
        random=@Random[
            serialVersionUID=@Long[3905348978240129619],
            seed=@AtomicLong[99287665085135],
            multiplier=@Long[25214903917],
            addend=@Long[11],
            mask=@Long[281474976710655],
            DOUBLE_UNIT=@Double[1.1102230246251565E-16],
            BadBound=@String[bound must be positive],
            BadRange=@String[bound must be greater than origin],
            BadSize=@String[size must be non-negative],
            seedUniquifier=@AtomicLong[-3282039941672302964],
            nextNextGaussian=@Double[0.0],
            haveNextNextGaussian=@Boolean[false],
            serialPersistentFields=@ObjectStreamField[][isEmpty=false;size=3],
            unsafe=@Unsafe[sun.misc.Unsafe@3c9f287f],
            seedOffset=@Long[24],
        ],
        illegalArgumentCount=@Integer[94039],
    ],
]           

觀察異常資訊的例子

$ watch demo.MathGame primeFactors "{params[0],throwExp}" -e -x 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 33 ms.
ts=2019-06-14 06:38:20; [cost=0.3491ms] result=@ArrayList[
    @Integer[-111033],
    java.lang.IllegalArgumentException: number is: -111033, need >= 2
        at demo.MathGame.primeFactors(MathGame.java:46)
        at demo.MathGame.run(MathGame.java:24)
        at demo.MathGame.main(MathGame.java:16)
,
]
ts=2019-06-14 06:38:21; [cost=0.0673ms] result=@ArrayList[
    @Integer[-150545],
    java.lang.IllegalArgumentException: number is: -150545, need >= 2
        at demo.MathGame.primeFactors(MathGame.java:46)
        at demo.MathGame.run(MathGame.java:24)
        at demo.MathGame.main(MathGame.java:16)
,
]           

trace

方法内部調用路徑,并輸出方法路徑上的每個節點上耗時,trace 指令能主動搜尋 class-pattern/method-pattern 對應的方法調用路徑,渲染和統計整個調用鍊路上的所有性能開銷和追蹤調用鍊路。

[j] 跳過JDK方法
指令執行次數
#cost 方法執行耗時
$ trace demo.MathGame run
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 44 ms.
`---ts=2019-06-14 06:42:24;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@70dea4e
    `---[0.6678ms] demo.MathGame:run()
        +---[0.0248ms] java.util.Random:nextInt() #23
        +---[0.1465ms] demo.MathGame:primeFactors() #24
        `---[0.1904ms] demo.MathGame:print() #25           

stack

輸出目前方法被調用的調用路徑,很多時候我們都知道一個方法被執行,但這個方法被執行的路徑非常多,或者你根本就不知道這個方法是從那裡被執行了,此時你需要的是 stack 指令。

$ stack demo.MathGame primeFactors
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 48 ms.
ts=2019-06-14 06:48:00;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@70dea4e
    @demo.MathGame.run()
        at demo.MathGame.main(MathGame.java:16)           

tt

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

-t 方法的每次執行情況
-n [n] 需要記錄的次數
-l 對現有記錄進行檢索
-s [express] 對現有記錄進行篩選
-i [index] 檢視調用資訊
-p 重做一次調用
--replay-times 指定調用次數
--replay-interval 指定調用間隔(機關ms, 預設1000ms)

記錄調用

$ tt -t demo.MathGame primeFactors
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 48 ms.
 INDEX      TIMESTAMP                   COST(ms)     IS-RET     IS-EXP      OBJECT              CLASS                                     METHOD
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
 1038       2019-06-14 07:13:02         0.4279       true       false       0x5197848c          MathGame                                  primeFactors
 1039       2019-06-14 07:13:03         16.4051      true       false       0x5197848c          MathGame                                  primeFactors
 1040       2019-06-14 07:13:04         0.067        true       false       0x5197848c          MathGame                                  primeFactors
 1041       2019-06-14 07:13:05         0.0933       true       false       0x5197848c          MathGame                                  primeFactors
 1042       2019-06-14 07:13:06         0.5713       true       false       0x5197848c          MathGame                                  primeFactors           
  • INDEX: 時間片段記錄編号,每一個編号代表着一次調用,後續tt還有很多指令都是基于此編号指定記錄操作,非常重要。
  • TIMESTAMP: 方法執行的本機時間,記錄了這個時間片段所發生的本機時間
  • COST(ms): 方法執行的耗時
  • IS-RET: 方法是否以正常傳回的形式結束
  • IS-EXP: 方法是否以抛異常的形式結束
  • OBJECT: 執行對象的hashCode(),注意,曾經有人誤認為是對象在JVM中的記憶體位址,但很遺憾他不是。但他能幫助你簡單的标記目前執行方法的類實體
  • CLASS: 執行的類名
  • METHOD: 執行的方法名

對記錄進行檢索

$ tt -l
 INDEX      TIMESTAMP                   COST(ms)     IS-RET     IS-EXP      OBJECT              CLASS                                     METHOD
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1038       2019-06-14 07:13:02         0.4279       true       false       0x5197848c          MathGame                                  primeFactors
 1039       2019-06-14 07:13:03         16.4051      true       false       0x5197848c          MathGame                                  primeFactors
 1040       2019-06-14 07:13:04         0.067        true       false       0x5197848c          MathGame                                  primeFactors
 1041       2019-06-14 07:13:05         0.0933       true       false       0x5197848c          MathGame                                  primeFactors
 1042       2019-06-14 07:13:06         0.5713       true       false       0x5197848c          MathGame                                  primeFactors           

對記錄進行篩選

$ tt -s 'method.name=="primeFactors"'
 INDEX      TIMESTAMP                   COST(ms)     IS-RET     IS-EXP      OBJECT              CLASS                                     METHOD
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1038       2019-06-14 07:13:02         0.4279       true       false       0x5197848c          MathGame                                  primeFactors
 1039       2019-06-14 07:13:03         16.4051      true       false       0x5197848c          MathGame                                  primeFactors
 1040       2019-06-14 07:13:04         0.067        true       false       0x5197848c          MathGame                                  primeFactors
 1041       2019-06-14 07:13:05         0.0933       true       false       0x5197848c          MathGame                                  primeFactors
 1042       2019-06-14 07:13:06         0.5713       true       false       0x5197848c          MathGame                                  primeFactors           
$ tt -i 1038
 INDEX          1038
 GMT-CREATE     2019-06-14 07:13:02
 COST(ms)       0.4279
 OBJECT         0x5197848c
 CLASS          demo.MathGame
 METHOD         primeFactors
 IS-RETURN      true
 IS-EXCEPTION   false
 PARAMETERS[0]  @Integer[190307]
 RETURN-OBJ     @ArrayList[
                    @Integer[13],
                    @Integer[14639],
                ]           
$ tt -i 1038 -p
 RE-INDEX       1038
 GMT-REPLAY     2019-06-14 07:22:58
 OBJECT         0x5197848c
 CLASS          demo.MathGame
 METHOD         primeFactors
 PARAMETERS[0]  @Integer[190307]
 IS-RETURN      true
 IS-EXCEPTION   false
 COST(ms)       0.414
 RETURN-OBJ     @ArrayList[
                    @Integer[13],
                    @Integer[14639],
                ]
Time fragment[1038] successfully replayed 1 times.           

jad/mc/redefine

  • jad(反編譯指定已加載類的源碼)
  • mc(Memory Compiler/記憶體編譯器,編譯.java檔案生成.class)
  • redefine (加載外部的.class檔案,redefine jvm已加載的類)
組合使用這三個指令可以實作線上熱更新代碼!

Step1 jad指令反編譯到磁盤檔案

jad --source-only demo.MathGame > /tmp/MathGame.java           

Step2 使用文本編輯器修改代碼

vi /tmp/MathGame.java           
public static void print(int number, List<Integer> primeFactors) {
        StringBuffer sb = new StringBuffer("" + number + "=");
        Iterator<Integer> iterator = primeFactors.iterator();
        while (iterator.hasNext()) {
            int factor = iterator.next();
            sb.append(factor).append('*');
        }
        if (sb.charAt(sb.length() - 1) == '*') {
            sb.deleteCharAt(sb.length() - 1);
        }
        System.out.println("MyTest.......");
    }           

Step3 mc指令來記憶體編譯修改過的代碼

$ mc /tmp/MathGame.java -d /tmp
Memory compiler output:
/tmp/demo/MathGame.class           

Step4 用redefine指令加載新的位元組碼

$ redefine /tmp/demo/MathGame.class
redefine success, size: 1           

現在看一下程式日志

illegalArgumentCount:96218, number is: -169877, need >= 2
illegalArgumentCount:96219, number is: -57731, need >= 2
MyTest.......
illegalArgumentCount:96220, number is: -207843, need >= 2
illegalArgumentCount:96221, number is: -193695, need >= 2
MyTest.......
illegalArgumentCount:96222, number is: -19514, need >= 2
illegalArgumentCount:96223, number is: -199441, need >= 2
illegalArgumentCount:96224, number is: -110791, need >= 2
MyTest.......
illegalArgumentCount:96225, number is: -116154, need >= 2
MyTest.......
MyTest.......
MyTest.......
MyTest.......
MyTest.......
MyTest.......           

果然神器~~~