基于Arthas的Java应用线上诊断方法
引言
针对Java应用在生产环境下出现的问题,通常开发者想通过远程debug的方式来排查问题是不可行的。一是私有云的客户系统无法连接去调试,连接上也会由于debug断点导致所有业务请求被暂停。二是需要额外添加日志来部署到线上观察,效率非常低下。当然,线上问题远不止这些,还需要结合Linux和JVM指令来监控系统整体运行指标。
因此,为了能够解决上述常规排查手段的弊端,这里介绍一款开源的Java诊断工具Arthas,该工具提供了jvm整体监控、线程堆栈、类加载器检查、方法级别的监控等丰富的操作,来定位在线Java应用问题。另外,值得注意的是由于该工具采用字节码植入方式,对于应用的运行性能和安全性需要考虑一下。
使用说明
安装
下载arthas-packing-xxx-bin.zip
直接解压,进行本地安装,默认安装在user.home
的用户目录下面。
- 启动:
执行java –jar arthas-boot.jar
,进入交互式命令行界面,选择需要监控的java应用进程号,控制台打印attach process xxx success 表示连接成功,输入help显示指令帮助说明,表示可以开始正式使用了。注意:如果没有第一步的本地安装,执行启动命令,会访问网络下载安装,内网用户将会无法使用。 - 卸载:
标准安装的话,安装目录在~/.arthas
,执行rm –rf ~/.arthas
即可。否则直接删除当前所在目录的sh和jar。
使用
基础命令
help
——查看命令帮助信息cls
——清空当前屏幕区域session
——查看当前会话的信息reset
——重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类version
——输出当前目标 Java 进程所加载的 Arthas 版本号history
——打印命令历史quit
——退出当前 Arthas 客户端,其他 Arthas 客户端不受影响shutdown
——关闭 Arthas 服务端,所有 Arthas 客户端全部退出keymap
——Arthas快捷键列表及自定义快捷键jvm相关
dashboard
——当前系统的实时数据面板thread
——查看当前 JVM 的线程堆栈信息jvm
——查看当前 JVM 的信息sysprop
——查看和修改JVM的系统属性sysenv
——查看JVM的环境变量getstatic
——查看类的静态属性class
/classloader
相关sc
——查看JVM已加载的类信息sm
——查看已加载类的方法信息dump
——dump 已加载类的 byte code 到特定目录redefine
——加载外部的.class文件,redefine到JVM里jad
——反编译指定已加载类的源码classloader
——查看classloader的继承树,urls,类加载信息,使用classloader去getResourcemonitor/watch/trace
相关
请注意,这些命令,都通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,因此在线上、预发使用时,请尽量明确需要观测的类、方法以及条件,诊断结束要执行shutdown
或将增强过的类执行reset
命令。monitor
——方法执行监控watch
——方法执行数据观测trace
——方法内部调用路径,并输出方法路径上的每个节点上耗时stack
——输出当前方法被调用的调用路径tt
——方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测options
options
——查看或设置Arthas全局开关管道
Arthas支持使用管道对上述命令的结果进行进一步的处理,如sm org.apache.log4j.Logger | grep <init>
grep——搜索满足条件的结果
plaintext——将命令的结果去除颜色
wc——按行统计输出结果后台异步任务
当线上出现偶发的问题,比如需要watch某个条件,而这个条件一天可能才会出现一次时,异步后台任务就派上用场了
使用>
将结果重写向到日志文件,使用&
指定命令是后台运行,session断开不影响任务执行(生命周期默认为1天)jobs
——列出所有jobkill
——强制终止任务fg
——将暂停的任务拉到前台执行bg
——将暂停的任务放到后台执行ognl相关官方文档
一般配合watch指令使用,其中的express、condition-express 采用ognl表达式
用于监控成员方法和类级别静态方法的入参(params
)、返回值(returnObj
)、抛出异常(throwExp
)等。示例:执行一次查看Test类的test方法的第一个参数(list)的第一个对象(pojo)中的age属性
watch Test test params[0].get(0).age -n 1
//还可以通过下标的方式访问params[0][0][“age”] ,这个写法等效于params[0][0].age执行一次查看Test类的test方法的第一个参数(list)的所有对象(pojo)中age>5的姓名
watch Test test "params[0].{? #this.age > 5}.{name}" -n 1
//那如果要找到第一个age大于5的Pojo的name,可以用^ 或$ 来进行第一个或最后一个的匹配执行一次查看Test类中的静态变量m
watch Test test '@Test@m' -n 1
应用场景
定位内存泄露、资源未释放等问题:
查看系统整体运行情况,包括线程概览、堆与非堆内存使用、GC次数与耗时、操作系统等信息dashboard –i 10000
//十秒钟刷新一次定位有问题的处理逻辑
查看前3个占用cpu比较高的线程堆栈thread –n 3
定位jar包冲突的问题
查看使用的类所在的jar以及类加载器sc –d 全路径类名`
性能测试,统计方法的执行平均耗时、成功/失败次数统计
确认要统计的业务类中的方法名monitor 全路径类名 方法名 -c 1
定位耗时的业务逻辑处理,从而有针对性地优化
通过trace方法查看指定方法的调用堆栈和最耗时的方法trace全路径类名 方法名
查看方法的入参和返回值,确认业务逻辑处理的正确性
通过watch和ognl表达式来检测结果watch 全路径类名 方法名 ‘params[0] + “, ”+ returnObj’
总结
以上介绍了Arthas是如何解决以往线上Java应用程序诊断的不足带来的问题,用好这些命令还需要多多结合实际问题去实践。最重要的,还是需要具备程序运行诊断的思路,工具只是辅助我们采集想要的信息,并且需要具备从这些信息中分析出问题,给出解决方案的能力。