Arthas是啥

当我们系统遇到JVM或者内存溢出等问题的时候,如何对我们的程序进行有效的监控和排查,就发现了几个比较常用的工具,比如JDK自带的 jconsole、jvisualvm还有一个最好用的工具——jprofiler,但是这个是收费的,或者除了很有钱的公司,一般很少人会用这个,还有一个就是我们今天的主角——Arthas ,为什么今天会重点讲这个呢?

官网地址:http://arthas.gitee.io/

GitHub地址:https://github.com/alibaba/arthas/

Arthas 是Alibaba开源的Java诊断工具,采用命令行交互模式,提供了较为丰富的功能,主要还是他是免费里面的算是好用且功能比较强大的一个JVM排查的插件,在了解这个利器之后,发现还是挺好用的,而且支持的功能也比较全面,那么Arthas到底可以为我们做哪些事情呢?

  1. 提供性能看板,包括线程、cpu、内存等信息,并且会定时的刷新。
  2. 根据各种条件查看线程快照。找出cpu占用率最高的n个线程
  3. 输出jvm的各种信息,如gc算法、jdk版本、ClassPath等
  4. 遇到问题无法在线上 debug,热部署加日志直接替换
  5. 查看某个类的静态属性,也可以通过ognl语法执行一些语句
  6. 查看已加载的类的详细信息,这个类从哪个jar包加载的,查看类的方法的信息
  7. dump 类的字节码到指定目录
  8. 直接反编译指定的类
  9. 快速定位应用的热点,生成火焰图
  10. 可以监控到JVM的实时运行状态

以前,你碰到这些问题,解决的办法大多是,修改代码,重新上线。但是在大公司里,上线的流程是非常繁琐的,如果为了多加一行日志而重新发布版本,无疑是非常折腾人的。但是阿里巴巴开源的Arthas

有了更为优雅的线上调试方法。

Arthas 支持JDK6,同时可以在 Linux/Mac/Windows上运行,自动Tab 补全功能,更方便我们定位问题和诊断

下载地址:https://arthas.gitee.io/download.html

你可以下载zip的包我下载的是arthas-packaging-3.5.0-bin.zip

或者通过命令去下载

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

使用手册

1. 快速启动

当我们下载好之后,我们直接通过命令启动就可以java -jar arthas-boot.jar,但是在此之前我们需要通过检测的代码来挂靠到Arthas上面

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; public class FullGCTest { //模拟银行卡的类
private static class CardInfo {
//小农的银行卡信息记录
BigDecimal price = new BigDecimal(10000000.0);
String name = "牧小农";
int age = 18;
Date birthdate = new Date(); public void m() {}
} //线程池 定时线程池
//50个,然后设置 拒绝策略
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
new ThreadPoolExecutor.DiscardOldestPolicy()); public static void main(String[] args) throws Exception {
executor.setMaximumPoolSize(50); for (;;){
modelFit();
Thread.sleep(100);
}
} /**
* 对银行卡进行风险评估
*/
private static void modelFit(){
List<CardInfo> taskList = getAllCardInfo();
//拿出每一个信息出来
taskList.forEach(info -> {
// do something
executor.scheduleWithFixedDelay(() -> {
//调用M方法
info.m(); }, 2, 3, TimeUnit.SECONDS);
});
} private static List<CardInfo> getAllCardInfo(){
List<CardInfo> taskList = new ArrayList<>();
//每次查询100张卡出来
for (int i = 0; i < 100; i++) {
CardInfo ci = new CardInfo();
taskList.add(ci);
} return taskList;
}
}

这个是上篇文章讲述的案例,感兴趣的可以了解一下。

首先我们需要使用javac 命令将Java文件进行编译javac FullGCTest.java进行编译,然后打印GC日志,进行风险监控打印GC日志:

java -Xms200M -Xmx200M -XX:+PrintGC FullGCTest

Arthas启动命令:java -jar arthas-boot.jar

我们就看到了我们刚才启动的FullGCTest的应用程序,我们输入编号 1 回车,这样我们就把Arthas挂靠到我们的程序上,接下来我们只需要做对应的命令操作就可以了

命令详情文档:https://arthas.aliyun.com/doc/commands.html

2. 功能列表

命令 详细说明
jvm 查看当前JVM信息
thread 查看当前JVM的线程堆栈信息
watch 方法执行数据观测
dashboard 当前系统的实时数据面板
trace 方法内部调用路径,并输出方法路径上的每个节点上耗时
stack 输出当前方法被调用的调用路径
tt 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
vmoption 查看,更新JVM已加载的类信息
sc 查看JVM已加载的类信息
sm 查看已加载类的方法信息
jad 反编译指定已加载类的源码
classloader 查看classloader的继承树,urls,类加载信息
heapdump 类似jmap命令的heap dump 功能

jvm

OPERATING-SYSTEM:系统相关参数

THREAD相关:

  • COUNT : JVM当前活跃的线程数
  • DAEMON-COUNT : JVM当前活跃的守护线程数
  • PEAK-COUNT: 从JVM启动开始曾经活着的最大线程数
  • STARTED-COUNT: 从JVM启动开始总共启动过的线程次数
  • DEADLOCK-COUNT: JVM当前死锁的线程数

MEMORY

FILE-DESCRIPTOR(文件描述符相关):

  • MAX-FILE-DESCRIPTOR-COUNT:JVM进程最大可以打开的文件描述符数
  • OPEN-FILE-DESCRIPTOR-COUNT:JVM当前打开的文件描述符数

thread 命令

参数说明:

命令 详细说明
id 线程id
[n:] 指定最忙的前N个线程并打印堆栈
[b] 找出当前阻塞其他线程的线程
[i] 指定cpu使用率统计的采样间隔,单位为毫秒,默认值为200
[--all] 显示所有匹配的线程

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

thread -n 3

thread 查看所有线程

thread 17: 显示指定线程的运行堆栈

thread -i: 指定采样时间间隔

thread -i 1000 : 统计最近1000ms内的线程CPU时间。

thread -n 3 -i 1000 : 列出1000ms内最忙的3个线程栈

dashboard 命令

运行程序时,会显示当前程序的实时信息,如qps, rt, 错误数, 线程池信息等等

数据说明:

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

参数说明:

参数名称 详细说明
id 刷新实时数据的时间间隔 (ms),默认5000ms
[n:] 刷新实时数据的次数

sc 命令

查看JVM已加载的类信息,通过SC我们可以看到我们这个类的详细信息,包括是从哪个jar包读取的,他是不是接口/枚举类等,甚至包括他是从哪个类加载器加载的。

参数说明:

参数名称 详细说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
[d] 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。如果一个类被多个ClassLoader所加载,则会出现多次
[E] 开启正则表达式匹配,默认为通配符匹配

sc -d *CardInfo: 打印类的详细信息

sc -d -f *CardInfo:打印类的Fiedld信息

heapdump + jhat分析

heapdump:类似于jmap命令

创建到指定文件夹下:

[arthas@365564]$ heapdump /usr/local/mxn/dump.hprof
Dumping heap to /usr/local/mxn/dump.hprof ...
Heap dump file created

创建成功后,我们就可以在指定文件夹下看到对应的dump文件,然后使用命令jhat dump.hprof,生成文件,成功后我们就可以通过IP+端口进行访问了

访问:

然后我们就可以通过IP+端口去访问它了,里面有个他的other,我们拉到最底下,找

Show instance counts for all classes (including platform)

从下面我们可以分析出来哪个类包含的对象最多,分析出来哪个类产生的对象

这个里面最强大的功能还是叫做 Execute Object Query Language (OQL) query,这个里面可以显示有哪些对象,对象有多少个字节和引用,可以观察到哪个对象产生了问题,如下图所示,显示所有String对应的对象



搜索点进去之后我们还能看到这个对象到底占用了多少个字节,有多少个引用指向了这个Object,这个OQL的语法也是很灵活,我们可以使用where条件去过滤

jad

jad:反编译某个类,或者反编译某个类的某个方法,动态代理生成类的问题定位 第三方的类(观察代码) 版本问题(确定自己最新提交的版本是不是被使用)

有人可能会问这个有啥用,源码我不是自己就知道吗?因为有时我们经常会不确定线上或者测试环境的包是否是我们修改过的,这时候就可以通过jad反编译来看下,是否是最新的代码

redafine

redafine:热替换,动态更新代码,不用重启jvm目前有些限制条件:只能改方法实现(方法已经运行完成),不能改方法名, 不能改属性 m() -> mm()

比如我们在线上环境有个class确认有问题,想要重新替换,一般情况下只能停掉服务器重新发布,在普通的小公司这样是可以的,但是在大规模公司京东淘宝这样的是不能停的,因为整个流程是非常复杂的,那怎么办呢?大家可以看到下面的案例

首先我们新建一个测试案例:

public class T{
public static void main(String[] args) throws Exception{
for(;;){
System.in.read();
new TT().m();
}
}
}
public class TT{
public void m(){
System.out.println(2);
}
}

使用命令javac *.java,编译成class文件,然后运行 T 文件

[root@VM-0-7-centos t]# java T
a
2
2

当我们输入a的时候打印2,但是我们上线以后才发现,我们需要输出的1,这个是如果要从本地更改要重新发布上线,为了这一个修改,明显是不值当的,但是如果我们用 redafine 热部署就可以帮助我们直接替换,不用重新发布jvm

然后我们将 T 这个程序挂靠到 Arthas 上面去

然后我们直接修改 TT.java 程序 vi TT.java,将里面打印2的值修改成1

public class TT{
public void m(){
System.out.println(1);
}
}

然后编译执行 ```javac TT.java ````

在回到我们挂靠的Arthas 上面执行 redefine /usr/local/mxn/fuccGc/t/TT.class

[arthas@398842]$ redefine /usr/local/mxn/fuccGc/t/TT.class
redefine success, size: 1, classes:
TT

执行成功

大家可以看到我们在没有重新启动的情况下成功替换了class文件

watch

watch:方法执行的数据观测,可以通过watch指令,来监控某个类,监控后,运行下你的功能,复现下场景,arthas会提供给你具体的出参和入参,帮助你排查故障

trace

输出方法调用路径,并输出耗时,这个指令对于优化代码非常的有用,可以看出具体每个方法执行的时间,如果是for循环等重复语句,还能看出n次循环中的最大耗时,最小耗时,和平均耗时,完美!

tt

在我们对某个方法开启tt后,会记录每一次调用(我们可以设置最大监控次数)的入参和返回参数,并能对这些不同时间下调进行观测

[arthas@405136]$ tt -t FullGCTest modelFit

命令参数解析-t

tt 命令有很多个主参数,-t 就是其中之一。这个参数的表明希望记录下类 *Test 的 print 方法的每次执行情况。

-n 3

当你执行一个调用量不高的方法时可能你还能有足够的时间用 CTRL+C 中断 tt 命令记录的过程,但如果遇到调用量非常大的方法,瞬间就能将你的 JVM 内存撑爆。

此时你可以通过 -n 参数指定你需要记录的次数,当达到记录次数时 Arthas 会主动中断tt命令的记录过程,避免人工操作无法停止的情况。

ognl表达式

ognl表达式

OGNL特殊用法请参考:https://github.com/alibaba/arthas/issues/71

OGNL表达式官方指南:https://commons.apache.org/proper/commons-ognl/language-guide.html

调用静态函数:ognl '@java.lang.System@out.println("hello")'

获取静态类的静态字段:ognl '@FullGCTest@random'

Arthas还支持Web Console,详见:https://alibaba.github.io/arthas/web-console.html

总结

Arthas是一个线上Debug神器,相比于其他工具,Arthas有着比较全面的功能,上手也比较容易,对于刚开始入门的小伙伴也是可以轻松掌握的,对于文中有不懂或者有问题的小伙伴,大家可以在下面留言评论。

原创不易,希望大家多多捧场,记得一键三连!!!

我是牧小农,怕什么真理无穷,进一步有进一步的欢喜,大家加油!

【死磕JVM】用Arthas排查JVM内存 真爽!我从小用到大的更多相关文章

  1. 记一次用arthas排查jvm中CPU占用过高问题

    记一次使用arthas排查jvm中CPU占用过高问题.这工具屌爆了 碾压我目前使用的全部JVM工具. 安装 小试 curl -O https://arthas.aliyun.com/arthas-bo ...

  2. 性能测试之JVM的故障排查-堆内存泄漏

    JVM异常说明(超链接) 一文中已介绍了,JVM每个运行时区域--程序计数器 .Java虚拟机栈.本地方法栈.Java堆.方法区.直接内存发生OutOfMemoryError的不同原因和不同错误信息. ...

  3. 排查JVM内存泄漏的命令

    1. jps 使用 jps -l -m 获取到当前jvm进程的pid,通过上述命令获取到了服务的进程号 jps(JVM Process Status Tool):显示指定系统内所有的HotSpot虚拟 ...

  4. 【死磕Java并发】-----Java内存模型之happend-before

    在上篇博客([死磕Java并发]-–深入分析volatile的实现原理)LZ提到过由于存在线程本地内存和主内存的原因,再加上重排序,会导致多线程环境下存在可见性的问题.那么我们正确使用同步.锁的情况下 ...

  5. 【死磕Java并发】-----Java内存模型之happens-before

    在上篇博客([死磕Java并发]-–深入分析volatile的实现原理)LZ提到过由于存在线程本地内存和主内存的原因,再加上重排序,会导致多线程环境下存在可见性的问题.那么我们正确使用同步.锁的情况下 ...

  6. 【转载】JVM 学习——垃圾收集器与内存分配策略

    本文主要是对<深入理解java虚拟机 第二版>第三章部分做的总结,文章中大部分内容都来自这章内容,也是博客 JVM 学习的第二部分. 简述 说到垃圾收集(Garbage Collectio ...

  7. JVM 基础:回收哪些内存/对象 引用计数算法 可达性分析算法 finalize()方法 HotSpot实现分析

    转自:https://blog.csdn.net/tjiyu/article/details/53982412 1-1.为什么需要了解垃圾回收 目前内存的动态分配与内存回收技术已经相当成熟,但为什么还 ...

  8. 【死磕Java并发】-----内存模型之happens-before

    在上篇博客([死磕Java并发]-----深入分析volatile的实现原理)LZ提到过由于存在线程本地内存和主内存的原因,再加上重排序,会导致多线程环境下存在可见性的问题.那么我们正确使用同步.锁的 ...

  9. Java(JVM运行时)各种内存区域详解及扩展

    本文整理于  Java内存与垃圾回收调优 Java 堆内存 从几个sample来学习Java堆,方法区,Java栈和本地方法栈 首先来一张图让我们理清楚java运行时状态: 诚然,如上图所示:java ...

随机推荐

  1. 【odoo14】第二十一章、性能优化

    通过odoo框架,我们可以开发大型且复杂的应用.良好的性能是实现这一目标的基础.本章,我们将探讨如何提高应用性能.同时,我们也会讲解找出影响性能的因素. 本章包含以下内容: 记录集的预读取模式 将数据 ...

  2. MyBatis的XML配置文件

    属性(properties) 通过properties的子元素设置配置项: <properties> <property name="driver" value= ...

  3. C# - 实现类型的比较

    IComparable<T> .NET 里,IComparable<T>是用来作比较的最常用接口. 如果某个类型的实例需要与该类型的其它实例进行比较或者排序的话,那么该类型就可 ...

  4. Ubuntu18.04安装MySQL(未设置密码或忘记密码)

    一 安装MySQL sudo apt-get update sudo apt-get install mysql-server 二 密码问题 1 安装时提示设置密码 这种情况没什么问题,通过已下命令登 ...

  5. docker使用常见问题解决方案:错误号码2058,docker WARNING :IPv4,容器间的通讯

    1.错误号码2058 1,错误解决: 解决方法:docker下mysql容器 登录 mysql -u root -p 登录你的 mysql 数据库,然后 执行这条SQL: ALTER USER 'ro ...

  6. 全网最详细的Linux命令系列-mkdir命令

    Linux mkdir 命令用来创建指定的名称的目录,要求创建目录的用户在当前目录中具有写权限,并且指定的目录名不能是当前目录中已有的目录. 命令格式: mkdir [选项] 目录... 命令功能: ...

  7. C#类的一些基础知识(静态方法可以不用实例化调用)

    将类成员函数声明为public static无需实例化即可调用类成员函数 using System; namespace ConsoleApp { class Program { static voi ...

  8. 可读性友好的JavaScript:两个专家的故事

    每个人都想成为专家,但什么才是专家呢?这些年来,我见过两种被称为"专家"的人.专家一是指对语言中的每一个工具都了如指掌的人,而且无论是否有帮助,都一定要用好每一点.专家二也知道每一 ...

  9. NLP入门学习中关于分词库HanLP导入使用教程

    大家好,时隔多年再次打开我的博客园写下自己的经验和学习总结,开园三年多,文章数少得可怜,一方面自己技术水平局限,另一方面是自己确实想放弃写博客.由于毕业工作的原因,经常性的加班以及仅剩下少的可怜的休息 ...

  10. vue 快速入门 系列 —— vue 的基础应用(下)

    其他章节请看: vue 快速入门 系列 vue 的基础应用(下) 上篇聚焦于基础知识的介绍:本篇聚焦于基础知识的应用. 递归组件 组件是可以在它们自己的模板中调用自身的.不过它们只能通过 name 选 ...