赞
踩
Arthas是一款阿里巴巴开源的 Java 线上诊断工具,功能非常强大,可以解决很多线上不方便解决的问题。
Arthas诊断使用的是命令行交互模式,支持JDK6+,Linux、Mac、Windows 操作系统,命令还支持使用 tab
键对各种信息的自动补全,诊断起来非常利索。
Arthas(阿尔萨斯)能为你做什么?
Arthas 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。
当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:
这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
是否有一个全局视角来查看系统的运行状况?
有什么办法可以监控到 JVM 的实时运行状态?
怎么快速定位应用的热点,生成火焰图?
怎样直接从 JVM 内查找某个类的实例?
Arthas 支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。
官方推荐使用 arthas-boot
进行安装,非常方便,以下是基于 Linux 系统环境进行演示,一般解决线上问题也是基于 Linux 环境。
第一步:下载
在任何目录下载 arthas-boot
这个包。
wget https://alibaba.github.io/arthas/arthas-boot.jar
第二步:启动
java -jar arthas-boot.jar --target-ip 0.0.0.0
默认情况下, arthas server 侦听的是 127.0.0.1
这个IP,如果希望远程可以访问,可以使用--target-ip
的参数
arthas-boot
是 Arthas
的启动程序,它启动后,会列出所有的 Java 进程,输入需要诊断的目标进程序号即可。
Arthas支持通过 Web Socket来连接。
当在本地启动时,可以访问 http://127.0.0.1:8563/
,通过浏览器来使用 Arthas。
输入 help
可以看到常用的帮助命令:
arthas-boot.jar
支持很多参数,可以执行 java -jar arthas-boot.jar -h
查看
第三步:选择进程
运行 arthas-boot
后,控制台会显示所有 Java 进程,选择一个你需要诊断的进程。
如第二步所示,这里有只有一个 Java 进程,输入序号1,回车,Arthas会附到目标进程上,并输出日志:
- [root@ofp-ofppricefeed-app-6ffbf6fc85-vz8qx arthas]# java -jar arthas-boot.jar
- [INFO] arthas-boot version: 3.5.0
- [INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
- * [1]: 27 org.apache.catalina.startup.Bootstrap
至此安装启动完成
启动完成后,当前光标会进入 arthas
的控制台,接受各种操作命令。
输入 dashboard 显示当前系统的实时数据面板,按 ctrl+c 即可退出。
数据说明:
查看当前 JVM 的线程堆栈信息。
查看 JVM 已加载的类详细信息。
Search-Class
的简写,这个命令能搜索出所有已经加载到 JVM 中的 Class 信息,sc -d *MathGame
如果搜索的是接口,还会搜索所有的实现类。比如查看所有的 Filter
实现类:
sc javax.servlet.Filter
-d
参数可以打印出类加载的具体信息,方便定位类加载问题
sc
支持通配符,比如搜索所有的 StringUtils
:
sc *StringUtils
打印类的 Field
信息:
sc -d -f demo.MathGame
查看已加载类的方法信息
Search-Method
的简写,这个命令能搜索出所有已经加载了 Class 信息的方法信息。
sm 命令只能看到由当前类所声明 (declaring) 的方法,父类则无法看到
查看 String 类的全部方法:
sm java.lang.String
查看具体方法的信息:
sm java.lang.String toString
通过 -d
参数可以打印函数的具体属性,展示每个方法的详细信息():
sm -d java.math.RoundingMode
反编译指定已加载类的源代码
jad demo.MatthGame
方法内部调用路径,并输出方法路径上的每个节点上耗时
- package demo;
-
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Random;
- import java.util.concurrent.TimeUnit;
-
- public class MathGame {
- private static Random random = new Random();
- private int illegalArgumentCount = 0;
-
- public List<Integer> primeFactors(int number) {
- /*44*/ if (number < 2) {
- /*45*/ ++this.illegalArgumentCount;
- throw new IllegalArgumentException("number is: " + number + ", need >= 2");
- }
- ArrayList<Integer> result = new ArrayList<Integer>();
- /*50*/ int i = 2;
- /*51*/ while (i <= number) {
- /*52*/ if (number % i == 0) {
- /*53*/ result.add(i);
- /*54*/ number /= i;
- /*55*/ i = 2;
- continue;
- }
- /*57*/ ++i;
- }
- /*61*/ return result;
- }
-
- public static void main(String[] args) throws InterruptedException {
- MathGame game = new MathGame();
- while (true) {
- /*16*/ game.run();
- /*17*/ TimeUnit.SECONDS.sleep(1L);
- }
- }
-
- public void run() throws InterruptedException {
- try {
- /*23*/ int number = random.nextInt() / 10000;
- /*24*/ List<Integer> primeFactors = this.primeFactors(number);
- /*25*/ MathGame.print(number, primeFactors);
- }
- catch (Exception e) {
- /*28*/ System.out.println(String.format("illegalArgumentCount:%3d, ", this.illegalArgumentCount) + e.getMessage());
- }
- }
-
- public static void print(int number, List<Integer> primeFactors) {
- StringBuffer sb = new StringBuffer(number + "=");
- /*34*/ for (int factor : primeFactors) {
- /*35*/ sb.append(factor).append('*');
- }
- /*37*/ if (sb.charAt(sb.length() - 1) == '*') {
- /*38*/ sb.deleteCharAt(sb.length() - 1);
- }
- /*40*/ System.out.println(sb);
- }
- }
-
查询某个方法被执行的时候的调用路径
方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测。
watch 虽然很方便和灵活,但需要提前想清楚观察表达式的拼写,这对排查问题而言要求太高,因为很多时候我们并不清楚问题出自于何方,只能靠蛛丝马迹进行猜测。这个时候如果能记录下当时方法调用的所有入参和返回值、抛出的异常会对整个问题的思考与判断非常有帮助。于是乎,TimeTunnel 命令就诞生了。
**tt -t -n 3 com.example.demo.arthas.user.UserController findUserById **
tt -l
tt -i 1001 (1001是INDEX的值)
对某个方法的调用进行定时监控。
monitor cn.javastack.springbootbestpractice.web.JsonTest getUserInfo -c 5
-c 5:表示每5秒统计一次,统计周期,默认值为120秒。
监控维度说明:
监控项 | 说明 |
---|---|
timestamp | 时间戳 |
class | 类名 |
method | 方法名 |
total | 调用次数 |
success | 成功次数 |
fail | 失败次数 |
rt | 平均响应时间 |
fail-rate | 失败率 |
watch
命令可以查看函数的:
watch demo.MathGame primeFactors returnObj
watch com.example.demo.arthas.user.UserController * '{params, throwExp}' -x 2
-x 2
是为了将结果展开返回值表达式实际是一个 ognl
表示,支持一些内置对象:
watch命令支持按请求耗时进行过滤:
watch com.example.demo.arthas.user.UserController * '{params, returnObj}' '#cost>200'
Arthas在 watch/trace 等命令时,实际上是修改了应用的字节码,插入增强的代码。显式执行 reset 命令,可以清除掉这些增强代码
再如
watch cn.javastack.springbootbestpractice.web.JsonTest getUserInfo ‘{params, returnObj}’ -x 2 -b
以上监控的是一个方法的入参情况,在方法执行前监控:-b,遍历深度:-x 2。
这个命令可以看到我们的java项目在运行时设置了哪些参数,命令没有参数时会打印所有的vm参数
命令可以查看当前JVM的系统属性(System Property)
通过getstatic命令可以方便的查看类的静态属性
Memory Compiler/内存编译器,编译.java文件生成.class。
加载外部的.class文件,retransform jvm已加载的类
退出当前 Arthas。
这个命令仅退出当前连接的客户端,附到目标进程上的 Arthas 会继续运行,端口不会关闭,下次连接时可以直接连接使用。
关闭 Arthas 服务端,退出所有 Arthas 客户端。
以上演示了 几个常用命令的基本使用,各种命令的使用详情可以在命令带 --help
进行查阅。
更多其他命令请参考:
https://alibaba.github.io/arthas/commands.html
热更新代码(修改正在运行的java程序)
注:修改jvm内存的代码需要注意两个点,第一不能添加新的字段信息,第二添加的代码如果方法还在执行就不会生效
1、下载测试用例
- # 下载测试用例
- wget https://github.com/hengyunabc/spring-boot-inside/raw/master/demo-arthas-spring-boot/demo-arthas-spring-boot.jar
- # 运行用例
- nohup java -jar demo-arthas-spring-boot.jar
2、把需要修改的类反编译出来
- # 使用jad 将jvm的class文件反编译成.java文件保存到临时目录
- jad --source-only com.example.demo.arthas.user.UserController > /fyx/tmp/UserController.java
3、退出arthas修改代码
- # 退出arthas
- stop
- # 进入目录
- cd /fyx/tmp
- # 修改代码
- vim UserController
- package com.example.demo.arthas.user;
-
- import com.example.demo.arthas.user.User;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class UserController {
- private static final Logger logger = LoggerFactory.getLogger(UserController.class);
-
- @GetMapping(value={"/user/{id}"})
- public User findUserById(@PathVariable Integer id) {
- logger.info("id: {}", (Object)id);
- if (id != null && id < 1) {
- logger.info("返回的数值有问题{}",id);
- return new User(id.intValue(),"name"+id);
- }
- return new User(id.intValue(), "name" + id);
- }
- }
-
-
4、查看UserController的类加载器的hashcode
- #进入arthas查看UserController的hash码
- [arthas@10181]$ sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash
- classLoaderHash 5674cd4d
5、将修改的代码编译成字节码并添加类加载器
- # -c <类加载器hashcode> <需要编译的路径> -d <字节码生成的路径>
- mc -c 5674cd4d /fyx/tmp/UserController.java -d /fyx/tmp/
6、加载更改的代码
retransform /fyx/tmp/com/example/demo/arthas/user/UserController.class
7、访问修改的代码
发现我们新添加的代码是有效的
如果arthas使用完后一定要使用stop命令推出程序,不然下次监听其他服务的时候就会监听不到。解决方案就是重新进上一次的监听的程序再执行一次stop命令。
总结下来,使用 Arthas 可以很方便的诊断一个 Java 应用程序,如:系统数据面板、JVM实时运行状态、类加载情况、监控方法执行情况、显示方法执行路径等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。