当前位置:   article > 正文

开发工具——Arthas使用教程

开发工具——Arthas使用教程

摘要

Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。

通常,本地开发环境无法访问生产环境。如果在生产环境中遇到问题,则无法使用 IDE 远程调试。更糟糕的是,在生产环境中调试是不可接受的,因为它会暂停所有线程,导致服务暂停。

开发人员可以尝试在测试环境或者预发环境中复现生产环境中的问题。但是,某些问题无法在不同的环境中轻松复现,甚至在重新启动后就消失了。

如果您正在考虑在代码中添加一些日志以帮助解决问题,您将必须经历以下阶段:测试、预发,然后生产。这种方法效率低下,更糟糕的是,该问题可能无法解决,因为一旦 JVM 重新启动,它可能无法复现,如上文所述。

Arthas 旨在解决这些问题。开发人员可以在线解决生产问题。无需 JVM 重启,无需代码更改。 Arthas 作为观察者永远不会暂停正在运行的线程。

1. Arthas的功能

当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到 JVM 的实时运行状态?
  7. 怎么快速定位应用的热点,生成火焰图?
  8. 怎样直接从 JVM 内查找某个类的实例?

Arthas 支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

2. 快速入门

2.1. 启动 math-game

  1. curl -O https://arthas.aliyun.com/math-game.jar
  2. java -jar math-game.jar

2.2. 启动 arthas

在命令行下面执行(使用和目标进程一致的用户启动,否则可能 attach 失败):

  • 执行该程序的用户需要和目标进程具有相同的权限。比如以admin用户来执行:sudo su admin && java -jar arthas-boot.jarsudo -u admin -EH java -jar arthas-boot.jar
  • 如果 attach 不上目标进程,可以查看~/logs/arthas/ 目录下的日志。
  • 如果下载速度比较慢,可以使用 aliyun 的镜像:java -jar arthas-boot.jar --repo-mirror aliyun --use-http
  • java -jar arthas-boot.jar -h 打印更多参数信息。
  1. curl -O https://arthas.aliyun.com/arthas-boot.jar
  2. java -jar arthas-boot.jar
  1. $ $ java -jar arthas-boot.jar
  2. * [1]: 35542
  3. [2]: 71560 math-game.jar

math-game进程是第 2 个,则输入 2,再输入回车/enter。Arthas 会 attach 到目标进程上,并输出日志:

  1. [INFO] Try to attach process 71560
  2. [INFO] Attach process 71560 success.
  3. [INFO] arthas-client connect 127.0.0.1 3658
  4. ,---. ,------. ,--------.,--. ,--. ,---. ,---.
  5. / O \ | .--. ''--. .--'| '--' | / O \ ' .-'
  6. | .-. || '--'.' | | | .--. || .-. |`. `-.
  7. | | | || |\ \ | | | | | || | | |.-' |
  8. `--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'
  9. wiki: https://arthas.aliyun.com/doc
  10. version: 3.0.5.20181127201536
  11. pid: 71560
  12. time: 2018-11-28 19:16:24
  13. $

2.3. 查看 dashboard

  1. $ dashboard
  2. ID NAME GROUP PRIORI STATE %CPU TIME INTERRU DAEMON
  3. 17 pool-2-thread-1 system 5 WAITIN 67 0:0 false false
  4. 27 Timer-for-arthas-dashb system 10 RUNNAB 32 0:0 false true
  5. 11 AsyncAppender-Worker-a system 9 WAITIN 0 0:0 false true
  6. 9 Attach Listener system 9 RUNNAB 0 0:0 false true
  7. 3 Finalizer system 8 WAITIN 0 0:0 false true
  8. 2 Reference Handler system 10 WAITIN 0 0:0 false true
  9. 4 Signal Dispatcher system 9 RUNNAB 0 0:0 false true
  10. 26 as-command-execute-dae system 10 TIMED_ 0 0:0 false true
  11. 13 job-timeout system 9 TIMED_ 0 0:0 false true
  12. 1 main main 5 TIMED_ 0 0:0 false false
  13. 14 nioEventLoopGroup-2-1 system 10 RUNNAB 0 0:0 false false
  14. 18 nioEventLoopGroup-2-2 system 10 RUNNAB 0 0:0 false false
  15. 23 nioEventLoopGroup-2-3 system 10 RUNNAB 0 0:0 false false
  16. 15 nioEventLoopGroup-3-1 system 10 RUNNAB 0 0:0 false false
  17. Memory used total max usage GC
  18. heap 32M 155M 1820M 1.77% gc.ps_scavenge.count 4
  19. ps_eden_space 14M 65M 672M 2.21% gc.ps_scavenge.time(m 166
  20. ps_survivor_space 4M 5M 5M s)
  21. ps_old_gen 12M 85M 1365M 0.91% gc.ps_marksweep.count 0
  22. nonheap 20M 23M -1 gc.ps_marksweep.time( 0
  23. code_cache 3M 5M 240M 1.32% ms)
  24. Runtime
  25. os.name Mac OS X
  26. os.version 10.13.4
  27. java.version 1.8.0_162
  28. java.home /Library/Java/JavaVir
  29. tualMachines/jdk1.8.0
  30. _162.jdk/Contents/Hom
  31. e/jre

2.4. thread 命令(查询进程信息)

参数名称

参数说明

id

线程 id

[n:]

指定最忙的前 N 个线程并打印堆栈

[b]

找出当前阻塞其他线程的线程

[i <value>

]

指定 cpu 使用率统计的采样间隔,单位为毫秒,默认值为 200

[--all]

显示所有匹配的线程

查看当前线程信息,查看线程的堆栈。这里的 cpu 使用率与 linux 命令top -H -p <pid> 的线程%CPU类似,一段采样间隔时间内,当前 JVM 里各个线程的增量 cpu 时间与采样间隔时间的比例。

  1. $ thread 1 | grep 'main('
  2. at demo.MathGame.main(MathGame.java:17)

2.4.1. thread id, 显示指定线程的运行堆栈

  1. $ thread 1
  2. "main" Id=1 WAITING on java.util.concurrent.CountDownLatch$Sync@29fafb28
  3. at sun.misc.Unsafe.park(Native Method)
  4. - waiting on java.util.concurrent.CountDownLatch$Sync@29fafb28
  5. at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
  6. at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
  7. at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997)
  8. at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)
  9. at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:231)

2.4.2. thread -b, 找出当前阻塞其他线程的线程:

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

2.4.3. 支持一键展示当前最忙的前 N 个线程并打印堆栈:

  1. $ thread -n 3
  2. "C1 CompilerThread0" [Internal] cpuUsage=1.63% deltaTime=3ms time=1170ms
  3. "arthas-command-execute" Id=23 cpuUsage=0.11% deltaTime=0ms time=401ms RUNNABLE
  4. at java.management@11.0.7/sun.management.ThreadImpl.dumpThreads0(Native Method)
  5. at java.management@11.0.7/sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:466)
  6. at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:199)
  7. at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:122)
  8. at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
  9. at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
  10. at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
  11. at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
  12. at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
  13. at java.base@11.0.7/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
  14. at java.base@11.0.7/java.util.concurrent.FutureTask.run(FutureTask.java:264)
  15. at java.base@11.0.7/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
  16. at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
  17. at java.base@11.0.7/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
  18. at java.base@11.0.7/java.lang.Thread.run(Thread.java:834)
  19. "VM Periodic Task Thread" [Internal] cpuUsage=0.07% deltaTime=0ms time=584ms

2.5. jad 来反编译(查询新代码是否生效)

  1. $ jad demo.MathGame
  2. ClassLoader:
  3. +-sun.misc.Launcher$AppClassLoader@3d4eac69
  4. +-sun.misc.Launcher$ExtClassLoader@66350f69
  5. Location:
  6. /tmp/math-game.jar
  7. /*
  8. * Decompiled with CFR 0_132.
  9. */
  10. package demo;
  11. import java.io.PrintStream;
  12. import java.util.ArrayList;
  13. import java.util.Iterator;
  14. import java.util.List;
  15. import java.util.Random;
  16. import java.util.concurrent.TimeUnit;
  17. public class MathGame {
  18. private static Random random = new Random();
  19. private int illegalArgumentCount = 0;
  20. public static void main(String[] args) throws InterruptedException {
  21. MathGame game = new MathGame();
  22. do {
  23. game.run();
  24. TimeUnit.SECONDS.sleep(1L);
  25. } while (true);
  26. }
  27. public void run() throws InterruptedException {
  28. try {
  29. int number = random.nextInt();
  30. List<Integer> primeFactors = this.primeFactors(number);
  31. MathGame.print(number, primeFactors);
  32. }
  33. catch (Exception e) {
  34. System.out.println(String.format("illegalArgumentCount:%3d, ", this.illegalArgumentCount) + e.getMessage());
  35. }
  36. }
  37. public static void print(int number, List<Integer> primeFactors) {
  38. StringBuffer sb = new StringBuffer("" + number + "=");
  39. Iterator<Integer> iterator = primeFactors.iterator();
  40. while (iterator.hasNext()) {
  41. int factor = iterator.next();
  42. sb.append(factor).append('*');
  43. }
  44. if (sb.charAt(sb.length() - 1) == '*') {
  45. sb.deleteCharAt(sb.length() - 1);
  46. }
  47. System.out.println(sb);
  48. }
  49. public List<Integer> primeFactors(int number) {
  50. if (number < 2) {
  51. ++this.illegalArgumentCount;
  52. throw new IllegalArgumentException("number is: " + number + ", need >= 2");
  53. }
  54. ArrayList<Integer> result = new ArrayList<Integer>();
  55. int i = 2;
  56. while (i <= number) {
  57. if (number % i == 0) {
  58. result.add(i);
  59. number /= i;
  60. i = 2;
  61. continue;
  62. }
  63. ++i;
  64. }
  65. return result;
  66. }
  67. }
  68. Affect(row-cnt:1) cost in 970 ms.

2.6. watch命令(函数调用输入输出)

通过watch命令来查看demo.MathGame#primeFactors函数的返回值:

  1. $ watch demo.MathGame primeFactors returnObj
  2. Press Ctrl+C to abort.
  3. Affect(class-cnt:1 , method-cnt:1) cost in 107 ms.
  4. ts=2018-11-28 19:22:30; [cost=1.715367ms] result=null
  5. ts=2018-11-28 19:22:31; [cost=0.185203ms] result=null
  6. ts=2018-11-28 19:22:32; [cost=19.012416ms] result=@ArrayList[
  7. @Integer[5],
  8. @Integer[47],
  9. @Integer[2675531],
  10. ]
  11. ts=2018-11-28 19:22:33; [cost=0.311395ms] result=@ArrayList[
  12. @Integer[2],
  13. @Integer[5],
  14. @Integer[317],
  15. @Integer[503],
  16. @Integer[887],
  17. ]
  18. ts=2018-11-28 19:22:34; [cost=10.136007ms] result=@ArrayList[
  19. @Integer[2],
  20. @Integer[2],
  21. @Integer[3],
  22. @Integer[3],
  23. @Integer[31],
  24. @Integer[717593],
  25. ]
  26. ts=2018-11-28 19:22:35; [cost=29.969732ms] result=@ArrayList[
  27. @Integer[5],
  28. @Integer[29],
  29. @Integer[7651739],
  30. ]
  • watch 命令定义了 4 个观察事件点,即 -b 函数调用前,-e 函数异常后,-s 函数返回后,-f 函数结束后
  • 4 个观察事件点 -b-e-s 默认关闭,-f 默认打开,当指定观察点被打开后,在相应事件点会对观察表达式进行求值并输出
  • 这里要注意函数入参函数出参的区别,有可能在中间被修改导致前后不一致,除了 -b 事件点 params 代表函数入参外,其余事件都代表函数出参
  • 当使用 -b 时,由于观察事件点是在函数调用前,此时返回值或异常均不存在
  • 在 watch 命令的结果里,会打印出location信息。location有三种可能值:AtEnterAtExitAtExceptionExit。对应函数入口,函数正常 return,函数抛出异常。

2.6.1. 观察函数调用返回时的参数、this 对象和返回值

  1. $ watch demo.MathGame primeFactors -x 2
  2. Press Q or Ctrl+C to abort.
  3. Affect(class count: 1 , method count: 1) cost in 32 ms, listenerId: 5
  4. method=demo.MathGame.primeFactors location=AtExceptionExit
  5. ts=2021-08-31 15:22:57; [cost=0.220625ms] result=@ArrayList[
  6. @Object[][
  7. @Integer[-179173],
  8. ],
  9. @MathGame[
  10. random=@Random[java.util.Random@31cefde0],
  11. illegalArgumentCount=@Integer[44],
  12. ],
  13. null,
  14. ]
  15. method=demo.MathGame.primeFactors location=AtExit
  16. ts=2021-08-31 15:22:58; [cost=1.020982ms] result=@ArrayList[
  17. @Object[][
  18. @Integer[1],
  19. ],
  20. @MathGame[
  21. random=@Random[java.util.Random@31cefde0],
  22. illegalArgumentCount=@Integer[44],
  23. ],
  24. @ArrayList[
  25. @Integer[2],
  26. @Integer[2],
  27. @Integer[26947],
  28. ],
  29. ]

2.6.2. 指定 Class 最大匹配数量

  1. $ watch demo.MathGame primeFactors -m 1
  2. Press Q or Ctrl+C to abort.
  3. Affect(class count: 1 , method count: 1) cost in 302 ms, listenerId: 3
  4. method=demo.MathGame.primeFactors location=AtExceptionExit
  5. ts=2022-12-25 19:58:41; [cost=0.222419ms] result=@ArrayList[
  6. @Object[][isEmpty=false;size=1],
  7. @MathGame[demo.MathGame@3bf400],
  8. null,
  9. ]
  10. method=demo.MathGame.primeFactors location=AtExceptionExit
  11. ts=2022-12-25 19:58:51; [cost=0.046928ms] result=@ArrayList[
  12. @Object[][isEmpty=false;size=1],
  13. @MathGame[demo.MathGame@3bf400],
  14. null,
  15. ]

2.6.3. 观察函数调用入口的参数和返回值

  1. $ watch demo.MathGame primeFactors "{params,returnObj}" -x 2 -b
  2. Press Ctrl+C to abort.
  3. Affect(class-cnt:1 , method-cnt:1) cost in 50 ms.
  4. ts=2018-12-03 19:23:23; [cost=0.0353ms] result=@ArrayList[
  5. @Object[][
  6. @Integer[-1077465243],
  7. ],
  8. null,
  9. ]

2.6.4. 同时观察函数调用前和函数返回后

  1. $ watch demo.MathGame primeFactors "{params,target,returnObj}" -x 2 -b -s -n 2
  2. Press Ctrl+C to abort.
  3. Affect(class-cnt:1 , method-cnt:1) cost in 46 ms.
  4. ts=2018-12-03 19:29:54; [cost=0.01696ms] result=@ArrayList[
  5. @Object[][
  6. @Integer[1],
  7. ],
  8. @MathGame[
  9. random=@Random[java.util.Random@522b408a],
  10. illegalArgumentCount=@Integer[13038],
  11. ],
  12. null,
  13. ]
  14. ts=2018-12-03 19:29:54; [cost=4.277392ms] result=@ArrayList[
  15. @Object[][
  16. @Integer[1],
  17. ],
  18. @MathGame[
  19. random=@Random[java.util.Random@522b408a],
  20. illegalArgumentCount=@Integer[13038],
  21. ],
  22. @ArrayList[
  23. @Integer[2],
  24. @Integer[2],
  25. @Integer[2],
  26. @Integer[5],
  27. @Integer[5],
  28. @Integer[73],
  29. @Integer[241],
  30. @Integer[439],
  31. ],
  32. ]

2.7. 退出 arthas

如果只是退出当前的连接,可以用quit或者exit命令。Attach 到目标进程上的 arthas 还会继续运行,端口会保持开放,下次连接时可以直接连接上。

如果想完全退出 arthas,可以执行stop命令。

3. Arthas常用命令

3.1. getstatic 查询类的静态属性

  1. # 使用方法为getstatic class_name field_name
  2. $ getstatic demo.MathGame random
  3. field: random
  4. @Random[
  5. serialVersionUID=@Long[3905348978240129619],
  6. seed=@AtomicLong[120955813885284],
  7. multiplier=@Long[25214903917],
  8. addend=@Long[11],
  9. mask=@Long[281474976710655],
  10. DOUBLE_UNIT=@Double[1.1102230246251565E-16],
  11. BadBound=@String[bound must be positive],
  12. BadRange=@String[bound must be greater than origin],
  13. BadSize=@String[size must be non-negative],
  14. seedUniquifier=@AtomicLong[-3282039941672302964],
  15. nextNextGaussian=@Double[0.0],
  16. haveNextNextGaussian=@Boolean[false],
  17. serialPersistentFields=@ObjectStreamField[][isEmpty=false;size=3],
  18. unsafe=@Unsafe[sun.misc.Unsafe@2eaa1027],
  19. seedOffset=@Long[24],
  20. ]

3.2. monitor(方法执行监控)

对匹配 class-patternmethod-patterncondition-express的类、方法的调用进行监控。monitor 命令是一个非实时返回命令. 实时返回命令是输入之后立即返回,而非实时返回的命令,则是不断的等待目标 Java 进程返回信息,直到用户输入 Ctrl+C 为止。

服务端是以任务的形式在后台跑任务,植入的代码随着任务的中止而不会被执行,所以任务关闭后,不会对原有性能产生太大影响,而且原则上,任何 Arthas 命令不会引起原有业务逻辑的改变。

监控项

说明

timestamp

时间戳

class

Java 类

method

方法(构造方法、普通方法)

total

调用次数

success

成功次数

fail

失败次数

rt

平均 RT

fail-rate

失败率

参数名称

参数说明

class-pattern

类名表达式匹配

method-pattern

方法名表达式匹配

condition-express

条件表达式

[E]

开启正则表达式匹配,默认为通配符匹配

[c:]

统计周期,默认值为 120 秒

[b]

方法调用之前计算 condition-express

[m <arg>]

指定 Class 最大匹配数量,默认值为 50。长格式为[maxMatch <arg>]

  1. $ monitor -c 5 demo.MathGame primeFactors
  2. Press Ctrl+C to abort.
  3. Affect(class-cnt:1 , method-cnt:1) cost in 94 ms.
  4. timestamp class method total success fail avg-rt(ms) fail-rate
  5. -----------------------------------------------------------------------------------------------
  6. 2018-12-03 19:06:38 demo.MathGame primeFactors 5 1 4 1.15 80.00%
  7. timestamp class method total success fail avg-rt(ms) fail-rate
  8. -----------------------------------------------------------------------------------------------
  9. 2018-12-03 19:06:43 demo.MathGame primeFactors 5 3 2 42.29 40.00%
  10. timestamp class method total success fail avg-rt(ms) fail-rate
  11. -----------------------------------------------------------------------------------------------
  12. 2018-12-03 19:06:48 demo.MathGame primeFactors 5 3 2 67.92 40.00%
  13. timestamp class method total success fail avg-rt(ms) fail-rate
  14. -----------------------------------------------------------------------------------------------
  15. 2018-12-03 19:06:53 demo.MathGame primeFactors 5 2 3 0.25 60.00%
  16. timestamp class method total success fail avg-rt(ms) fail-rate
  17. -----------------------------------------------------------------------------------------------
  18. 2018-12-03 19:06:58 demo.MathGame primeFactors 1 1 0 0.45 0.00%
  19. timestamp class method total success fail avg-rt(ms) fail-rate
  20. -----------------------------------------------------------------------------------------------
  21. 2018-12-03 19:07:03 demo.MathGame primeFactors 2 2 0 3182.72 0.00%

3.3. retransform(动态热加载新的文件)

类,类加载相关的命令

说明

sc

Search Class 查看运行中的类信息

sm

Search Method 查看类中方法的信息

jad

反编译字节码为源代码

mc

Memory Compile 将源代码编译成字节码

redefine

将编译好的字节码文件加载到jvm中运行

3.3.1.1. 下载jar 包

下载demo-arthas-spring-boot.jar(spring-boot-inside/demo-arthas-spring-boot/demo-arthas-spring-boot.jar at master · hengyunabc/spring-boot-inside · GitHub),再用java -jar命令启动:

  1. # 访问接口
  2. curl http://localhost:80/user/0
  3. # 返回结果
  4. {"timestamp":1550223186170,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"}
3.3.1.2. Arthas使用jad反编译UserController
jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java
  1. # 修改UserController文件
  2. vim /tmp/UserController.java
  3. # 文件如下
  4. @GetMapping(value={"/user/{id}"})
  5. public User findUserById(@PathVariable Integer id) {
  6. logger.info("retransform test id: {}", (Object)id);
  7. if (id != null && id < 1) {
  8. return new User(id, "retransform name" + id);
  9. // throw new IllegalArgumentException("id < 1");
  10. }
  11. return new User(id.intValue(), "name" + id);
  12. }
3.3.1.3. Arthas中使用sc查找加载UserController的ClassLoader
  1. # sc 命令
  2. sc -d *UserController | grep classLoaderHash
  3. # sc 命令结果
  4. $ sc -d *UserController | grep classLoaderHash
  5. classLoaderHash 1be6f5c3

可以发现是 spring boot LaunchedURLClassLoader@1be6f5c3 加载的。

注意hashcode是变化的,需要先查看当前的ClassLoader信息,提取对应ClassLoader的hashcode。

如果你使用-c,你需要手动输入hashcode:-c <hashcode>

对于只有唯一实例的ClassLoader可以通过--classLoaderClass指定class name,使用起来更加方便.

--classLoaderClass 的值是ClassLoader的类名,只有匹配到唯一的ClassLoader实例时才能工作,目的是方便输入通用命令,而-c <hashcode>是动态变化的。

3.3.1.4. Athas中使用mc 编译修改后的文件

保存好/tmp/UserController.java之后,使用mc(Memory Compiler)命令来编译,并且通过--classLoaderClass参数指定ClassLoader:

  1. # mc 编译修改的新的文件
  2. mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
  3. # 命令结果如下:
  4. $ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
  5. Memory compiler output:
  6. /tmp/com/example/demo/arthas/user/UserController.class
  7. Affect(row-cnt:1) cost in 346 ms
3.3.1.5. retransform加载新class 文件
  1. # retransform 命令
  2. retransform /tmp/com/example/demo/arthas/user/UserController.class
  3. # retransform 命令结果如下:
  4. $ retransform /tmp/com/example/demo/arthas/user/UserController.class
  5. retransform success, size: 1
3.3.1.6. 重新访问:

3.4. reset 重置增强类

重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端stop时会重置所有增强过的类。

  1. $ trace Test test
  2. Press Ctrl+C to abort.
  3. Affect(class-cnt:1 , method-cnt:1) cost in 57 ms.
  4. `---ts=2017-10-26 17:10:33;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc
  5. `---[0.590102ms] Test:test()
  6. `---ts=2017-10-26 17:10:34;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc
  7. `---[0.068692ms] Test:test()
  8. $ reset Test
  9. Affect(class-cnt:1 , method-cnt:0) cost in 11 ms.
  1. $ trace Test test
  2. Press Ctrl+C to abort.
  3. Affect(class-cnt:1 , method-cnt:1) cost in 15 ms.
  4. `---ts=2017-10-26 17:12:06;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@14dad5dc
  5. `---[0.128518ms] Test:test()
  6. # 还原所有类
  7. $ reset
  8. Affect(class-cnt:1 , method-cnt:0) cost in 9 ms.

博文参考

arthas/math-game/src/main/java/demo/MathGame.java at master · alibaba/arthas · GitHub

简介 | arthas

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/973277
推荐阅读
相关标签
  

闽ICP备14008679号