Arthas 是阿里开源的 Java 诊断工具。在线排查问题,无需重启;动态跟踪 Java 代码;实时监控 JVM 状态。Arthas 支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

Arthas可以通过简单的命令交互模式,接入运行的JVM,快速定位和诊断线上程序运行的问题。在不重启服务的情况下,实时,动态的修改相关代码,并实时生效,具体工作原理如下:

  1. 连接JVM,通过attach机制,通过attach pid连接正在运行的JVM
  2. 查看及修改JVM字节码,通过instrument技术对运行中的JVM附加或修改字节码来实现增强的逻辑

Arthas的执行过程如下:Arthas底层调用rt.jar包的ManagementFactory获取整个JVM内部信息,通过命令集成与后端交互,执行,返回结果,整个工程简单清晰,容易上手。

arthas-demo入门

可以使用阿里云给的基础教程地址练习: https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=arthas-basics

在这里,我使用自己的服务器,跟着基础教程做入门联系。

1.下载math-game.jar,再用java -jar命令启动

[root@localhost arthas]# wget https://arthas.aliyun.com/math-game.jar
[root@localhost arthas]# java -jar arthas-boot.jar

2.新开Terminal ,下载arthas-boot.jar,再用java -jar命令启动

[root@localhost arthas]# wget https://arthas.aliyun.com/arthas-boot.jar
[root@localhost arthas]# java -jar arthas-boot.jar

arthas-bootArthas的启动程序,它启动后,会列出所有的Java进程,用户可以选择需要诊断的目标进程。

选择第一个进程,输入 1 (math-game这个程序对应的就是1),再Enter/回车

Attach成功之后,会打印Arthas LOGO。输入 help 可以获取到更多的帮助信息。

3. dashboard 命令可以查看当前系统的实时数据面板

数据说明:

  • ID:java级别的线程ID,注意这个ID不能跟jstack中的nativeID一一对应
  • NAME:线程名
  • GROUP:线程组名
  • PRIORI:线程的优先级,1~10之间的数字,越大表示优先级越高
  • STATE:线程的状态
  • %CPU:线程的CPU使用率,比如采样间隔1000ms,某个线程的增量cpu时间为100ms,则cpu的使用率为100/1000=10%
  • DELTA_TIME:上次采样之后线程运行增量cpu时间,数据格式为秒
  • TIME:线程运行总CPU时间,数据格式为 分:秒
  • INTERRUPTED:线程当前的中断位状态
  • DAEMON:是否是daemon(后台)线程

4. thread 命令会打印线程ID 1的栈

还可以通过thread 1 | grep 'main(' 命令来查找main class:

参数说明:

参数名称 参数说明
id 线程id
[ n: ] 指定最忙的前N个线程并打印堆栈
[ b ] 找出当前阻塞其他线程的线程(目前只支持找出synchronized关键字阻塞住的线程, 如果是java.util.concurrent.Lock, 目前还不支持。)
[ i ] 指定cpu占比统计的采样间隔,单位为毫秒
[ --all ] 显示所有匹配的线程
[ --state ] 查看指定状态的线程,如: thread --state WAITING

5. sc命令来查找JVM里已加载的类

sc为“Search-Class” 的简写,能搜索出所有已经加载到 JVM 中的 Class 信息。

参数说明:

参数名称 参数说明
class-pattern 类名表达式匹配,支持全限定名。如:demo.MathGame,也支持demo/MathGame
method-pattern 方法名表达式匹配
[ d ] 输出当前类的详细信息,包括这个类所加载的原始文件来源,类的声明,加载的ClassLoader等详细信息;如果一个类被多个ClassLoader所加载,则会出现多次
[ E ] 开启正则表达式匹配,默认为通配符匹配
[ f ] 输出当前类的成员变量信息(需要配合参数 -d一起使用)
[ x: ] 指定输出静态变量时属性的遍历深度,默认为0,直接使用toString输出
[ c: ] 指定class的ClassLoader的hashcode
[ classLoaderClass: ] 指定执行表达式的ClassLoader的class name
[ n: ] 具有详细信息的匹配类的最大数量,默认为100

sm为“Search-Method” 的简写,查询某个类下所有的方法,与sc的功能类似,这里就不详细介绍了。

6. jad命令来反编译代码

还可以反编译指定的函数

反编译时只显示源代码

默认情况下,反编译结果里会带有ClassLoader信息,通过--source-only选项,可以只打印源代码。这样就会清爽很多。

7. watch方法执行数据观测

watch命令可以查看函数的参数/返回值/异常信息,通过编写 OGNL 表达式进行对应变量的查看。

从上面的结果里,说明函数被执行了两次,第一次结果是 location=AtExceptionExit,说明函数抛出了异常,因此returnObj是null;第二次结果是location=AtExit,说明函数正常返回,因此可以看到returnObj的结果是一个ArrayList。

参数说明:

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
express 观察表达式,默认值为:{params,target,returnObj},单个值可以不用加 {},多个值需要加
condition-express 条件表达式,不能加 {},可以使用逗号分隔子表达式,取表达式最后一个值来判断
[ b ] 在方法调用之前观察,默认关闭,由于观察事件点是在方法调用前,此时返回值或异常均不存在,params代表方法入参
[ e ] 在方法异常之后观察,默认关闭,params代表方法出参
[ s ] 在方法返回之后观察,默认关闭,params代表方法出参
[ f ] 在方法结束之后(正常返回和异常返回)观察,默认打开,params代表方法出参
[ E ] 开启正则表达式匹配,默认为通配符匹配
[ x: ] 指定输出结果的属性遍历深度,默认为1
[ #cost ] 监控耗时

条件表达式的例子:

下面这个例子表示只有参数小于0的调用才会响应。

异常信息的例子:

-e 表示抛出异常时才触发

express中,表示异常信息的变量时throwExp

8. vmtool命令,可以搜索内存对象

vmtool 利用JVMTI接口,实现查询内存对象,强制GC等功能。

参数说明:

参数名称 参数说明
--action getInstances 返回结果绑定到 instances变量上,它是数组。
--className 指定类名(完成路径),支持 java.lang.String,也支持java/lang/String
[ --limit ] 限制返回值数量,避免获取超大数据时对JVM造成压力。默认值是10。
[ -x ] 指定返回结果展开层数,默认为1
[ c: ] 指定class的ClassLoader的hashcode(通过sc命令找到加载class的classLoader)
[ classLoaderClass: ] 指定执行表达式的ClassLoader的class name

强制GC的命令:vmtool --action forceGc

9. 退出Arthas

exit 或者 quit 命令可以退出Arthas。退出Arthas之后,还可以再次用 java -jar arthas-boot.jar 来连接。

10. 彻底退出Arthas

exit/quit命令只是退出当前session,arthas server还在目标进程中运行。

想完全退出Arthas,可以执行 stop 命令。

Arthas的其他重点使用功能

1. mc 内存编译器(Memory Compiler/内存编译器)

Memory Compiler/内存编译器,编译.java文件生成.class。通过 -c / --classLoaderClass 参数指定classLoader,-d 参数指定输出目录;编译生成.class文件之后,可以结合retransform 命令实现热更新代码。retransform的限制:1.不允许新增加field和method 2.正在跑的函数,没有退出不能生效

这里还是使用arthas的提供的教程:https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-mc-retransform

1.由于正在跑的函数没有退出时不生效的,所以上面的math-game的demo就不能使用了,所以得下载另一个demo(一个简单的spring-boot应用)。

[root@localhost arthas]# wget https://raw.githubusercontent.com/hengyunabc/spring-boot-inside/master/demo-arthas-spring-boot/demo-arthas-spring-boot.jar
[root@localhost arthas]# java -jar demo-arthas-spring-boot.jar

2.新开一个Terminal;访问下面这个路径,可以看到报错(500异常)了。

3.启动arthas-boot应用。

4.反编译代码可以看到,当id小于1是,就会抛出异常。

在这里,我们修改文件,想让id小于1时还是能正常返回,不抛出异常。

5.jad反编译UserController,将结果保存在/tmp/UserController.java文件夹里。

[arthas@1645]$ jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java

6.通过vim /tmp/UserController.java编辑java类。

7.通过sc查找加载UserContoller的ClassLoader,也可以在jad时显示源码,里面也有classLoader的信息。

以下三个命令任意一个都可以。可以看到,这个java类是由LaunchedURLClassLoader@1be6f5c3 这个类加载器加载的。

8.通过mc命令编译,同时指定--classLoaderClass参数指定ClassLoader:

[arthas@1645]$ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp

也可以通过-c 参数指定ClassLoaderHash:

[arthas@1645]$ mc --c 1be6f5c3 /tmp/UserController.java -d /tmp

可以看到,在tmp文件夹下,根据UserController的类的全路径编译了一个class文件:

9.retransform 命令中西加载新编译好的UserContoller.class类:

10.重新编译文件可以看到,代码已经替换成最新的了:

11.访问:

显示已经替换过的类:retransform -l

恢复修改前的代码,清楚指定的类:retransform -d 1

清楚所有的:retransform --deleteAll

2.trace 方法内部调用路径,并输出方法路径上的每个节点上耗时

可以观察方法执行的时候哪个子调用比较慢

[arthas@1645]$ trace ClassName methodName

trace 命令能主动搜索 class-patternmethod-pattern 对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。在进行性能调优的时候十分有效。

参数说明:

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
express 观察表达式,默认值为:{params,target,returnObj},单个值可以不用加 {},多个值需要加
condition-express 条件表达式
[ E ] 开启正则表达式匹配,默认为通配符匹配
[ n: ] 命令执行次数
[ #cost ] 方法执行耗时
[ --skipJDKMethod ] 跳过jdk方法,默认为true

不跳过JDK方法:

只展示耗时大于1ms的调用路径:

动态trace:

从上图中可以看到,primeFactors的方法耗时最长,如果想深入primeFactors方法,可以打开一个新的终端,使用telnet localhost 3658 连接上arthas,在trace primeFactors时指定listenerId。

这时终端2打印的结果,说明已经增强了一个函数:Affect(class count: 1 , method count: 1),但不再打印更多的结果。

再查看终端1,可以发现trace的结果增加了一层,打印了primeFactors函数里的内容:

注意 --listenerId指定的id在前一条命令的输出中可以看到。

3.stack 查看某个函数的调用堆栈路径

很多时候,在一个方法被执行时,方法的执行路径非常多,或者根本就不着调这个方法时从哪里被执行的,就可以使用stack命令。此命令和trace命令结构类似。

[arthas@1645]$ stack demo.MathGame primeFactors

参数说明:

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
express 观察表达式,默认值为:{params,target,returnObj},单个值可以不用加 {},多个值需要加
condition-express 条件表达式
[ E ] 开启正则表达式匹配,默认为通配符匹配
[ n: ] 命令执行次数

4.tt 命令

tt是TimeTunnel 的缩写,tt命令记录方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测。

watch 虽然很方便和灵活,但需要提前想清楚观察表达式的拼写,这对排查问题而言要求太高,因为很多时候我们并不清楚问题出自于何方,只能靠蛛丝马迹进行猜测。

这个时候如果能记录下当时方法调用的所有入参和返回值、抛出的异常会对整个问题的思考与判断非常有帮助。

参数说明:

参数名称 参数说明
-t 记录下类对应的方法的每次执行情况
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
[ n: ] 命令执行次数
condition-express 条件表达式

表格字段说明:

表格字段 字段解释
index 时间片段记录编号,每一个编号代表着一次调用,后续tt还有很多命令都是基于此编号指定记录操作,非常重要。
timestamp 方法执行的本机时间,记录了这个时间片段所发生的本机时间
cost(ms) 方法执行的耗时
is-ret 方法是否以正常返回的形式结束
is-exp 方法是否以抛异常的形式结束
object 执行对象的hashCode(),注意,曾经有人误认为是对象在JVM中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体
class 执行的类名
method 执行的方法名

检索调用记录:

tt -l 检索所有的调用记录:

筛选出 primeFactors 方法的调用信息:

通过 -i 参数后边跟着对应的 INDEX 编号查看到他的详细信息:

重做一次调用:

tt 命令由于保存了当时调用的所有现场信息,所以我们可以自己主动对一个 INDEX 编号的时间片自主发起一次调用。此时需要使用 -p 参数。通过 --replay-times 指定 调用次数,通过 --replay-interval 指定多次调用间隔(单位ms, 默认1000ms)

需要强调的点

  1. ThreadLocal 信息丢失

    很多框架偷偷的将一些环境变量信息塞到了发起调用线程的 ThreadLocal 中,由于调用线程发生了变化,这些 ThreadLocal 线程信息无法通过 Arthas 保存,所以这些信息将会丢失。

  2. 引用的对象

    需要强调的是,tt 命令是将当前环境的对象引用保存起来,但仅仅也只能保存一个引用而已。如果方法内部对入参进行了变更,或者返回的对象经过了后续的处理,那么在 tt 查看的时候将无法看到当时最准确的值。这也是为什么 watch 命令存在的意义。

5.monitor 方法执行监控

对匹配的class-pattern / method-pattern的类、方法的调用进行监控。monitor命令是一个非实时返回的命令(并不是输入之后立即返回,而是不断的等待目标java进程返回信息)。

参数说明:

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
[ E ] 开启正则表达式匹配,默认为通配符匹配
[ c: ] 统计周期,默认值为120秒,是一个整型的参数值

监控项说明:

监控项 timestamp class method total success tail rt fail-rate
说明 时间戳 java类 方法 调用次数 成功次数 失败次数 平均rt 失败率

5.target-ip

target-ip 为指定绑定的IP,如果不指定IP,Arthas只listen 127.0.0.1,所以如果想从远程连接,则可以使用 --target-ip参数指定listen的IP。

java -jar arthas-boot.jar --target-ip IP

绑定远程访问IP后,可以在通过telnet 或者http的方式远程连接Arthas进行问题排查

还有更多的功能请查看arthas的官方文档。

arthas学习图文记录的更多相关文章

  1. Activiti 学习笔记记录(2016-8-31)

    上一篇:Activiti 学习笔记记录(二) 导读:上一篇学习了bpmn 画图的常用图形标记.那如何用它们组成一个可用文件呢? 我们知道 bpmn 其实是一个xml 文件

  2. Activiti 学习笔记记录(二)

    上一篇:Activiti 学习笔记记录 导读:对于工作流引擎的使用,我们都知道,需要一个业务事件,比如请假,它会去走一个流程(提交申请->领导审批---(批,不批)---->结束),Act ...

  3. PostgresSQL 学习资料记录处

    PostgresSQL 学习资料记录处  博客:http://francs3.blog.163.com PostgreSQL9.4 中文手册:http://www.postgres.cn/docs/9 ...

  4. Lucene.net(4.8.0) 学习问题记录五: JIEba分词和Lucene的结合,以及对分词器的思考

    前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移.因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3 ...

  5. Lucene.net(4.8.0) 学习问题记录六:Lucene 的索引系统和搜索过程分析

    前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移.因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3 ...

  6. Java NIO学习与记录(八): Reactor两种多线程模型的实现

    Reactor两种多线程模型的实现 注:本篇文章例子基于上一篇进行:Java NIO学习与记录(七): Reactor单线程模型的实现 紧接着上篇Reactor单线程模型的例子来,假设Handler的 ...

  7. ZigBee cc2530芯片学习 error记录(1)

    ZigBee cc2530芯片学习 error记录   Error[e46]: Undefined external "LcdInit" referred in main( xxx ...

  8. JVM 学习笔记记录

    JVM 学习笔记记录 Sun JDK 监控和故障处理工具 名称 主要作用 jps JVM Process Status Tool, 显示指定系统内所有的HotSpot虚拟机进程 jstat JVM S ...

  9. '"千"第一周学习情况记录

    一周过去了,今天将我这一周的学习内容和主要感想记录与此和大家共同分享,一起进步.我将自己的学习计划命名为"千",因为我喜欢这个字,希望能用此来鼓舞自己不断前进.时间总是很快的,这一 ...

随机推荐

  1. 记MyBaits-Plus 实现菜单的无限层关系

    Mybatis-Plus父子菜单 首先来看一下实现的效果 pojo层 @Data @TableName("platform_role") public class Role imp ...

  2. Python学习--21天Python基础学习之旅(Day03、Day04)

    关于缩进问题,缩进几个空格都不影响程序解释(不会报错什么的),但一般缩进四个空格是为了可读性和规范. Day03: Chapter 5 1.if语句 1.1条件测试:值为True或False的表达式成 ...

  3. C语言break,return

    C语言break,continue,return的相似与区别 相同点: 都改变了程序的执行流程 区别是:break    用于循环和switch分支,跳出它所在分支或循环体到它所在的模块的      ...

  4. 以太网EMC(浪涌)中心抽头方案(节约空间)

  5. 破界!Omi生态omi-mp发布,用小程序开发生成Web

    omi-mp 是什么 Omi 框架是微信支付线研发部和 AlloyTeam 开源的通用 Web 组件化框架,基于 Web Components,用来开发 PC.手机浏览器或者微信.手Q webview ...

  6. 【Unity原神AR开发实战 2022】下载原神模型,PMX转FBX,导入到Unity,AR设置,测试应用程序,生成应用程序

    文章目录 一.前言 二.模型下载 1.官网下载 2.模之屋官方下载 3.第三方链接 三.pmx转fbx 1.Blender插件CATS的下载与安装 2.pmx模型的导入 四.Unity开发部分 1.V ...

  7. Java学习笔记(韩顺平教育 b站有课程)

    Java重要特点 面向对象(oop) 健壮性:强类型机制,异常处理,垃圾的自动收集 跨平台性的 (一个编译好的.class可以在多个系统下运行) TEST.java -> TEST.class ...

  8. 对 rest 参数的理解

    扩展运算符被用在函数形参上时,它还可以把一个分离的参数序列整合成一个数组: function mutiple(...args) { let result = 1; for (var val of ar ...

  9. 常见的JVM 面试题

    1.讲一讲JVM的跨平台与跨语言 跨平台 我们写的一个类,在不同的操作系统上(Linux.windows.Mac OS)执行,效果是一样的.这就是JVM的跨平台性. 跨语言 JVM只识别字节码,JVM ...

  10. C++---条件结构和循环结构

    条件结构 if结构 #include <iostream> using namespace std; int main() { //判断用户输入的字符是否是合法的硬盘盘符A-Z char ...