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. 【ZeyFraのJavaEE开发小知识05】Mybatis-Plus & Axios

    关于如何在Mybatis-Plus中添加SQL拦截器 之前ZeyFra在MyBatis-Plus[踩坑记录01]一文中提到过,使用Mybatis-Plus时最好使用MybatisSqlSessionF ...

  2. 开灯问题3_2(JAVA语言)

    package 第三章; public class 开灯问题3_2 { public static void main(String[] args) { // TODO Auto-generated ...

  3. certutil绕过

    一般进内网过后我都会使用certutil下载文件,但在最近打一台内网机子的时候出现了certutil拒绝访问的情况,在本地搭建了一个环境尝试绕过certutil下载文件. 安装杀软更新到最新版本,开启 ...

  4. NetCore的缓存使用详例

    关于我 作者博客|文章首发 缓存基础知识 缓存可以减少生成内容所需的工作,从而显著提高应用程序的性能和可伸缩性. 缓存最适用于不经常更改的 数据,生成 成本很高. 通过缓存,可以比从数据源返回的数据的 ...

  5. 问题笔记 - element表格 操作状态值

    1.必须从传到表里的数据源中取值(scope.row.star)

  6. Mysql多表合并以及连接问题

    目的 1.为了备战过两天的面试,我又重新给孙老师的课件看了一遍,学累了,就写写自己的新的体会,和遇到的问题,来进行一个记录,这是知识产出的过程,据说可以帮助我学习,看视频什么的都是被动学习,不进行及时 ...

  7. [Fundamental of Power Electronics]-PART I-1.引言-1.2 1.3 电力电子技术的几个应用、本书内容

    1.2 电力电子技术的几个应用 高效开关变换器面临的功率范围从 (1)小于1瓦(电池供电的便携式设备内的DC-DC转换器)到(2)计算机及办公设备中的几十,几百,数千瓦到(3)变速电机驱动器中上千瓦及 ...

  8. python基础(十一):集合的使用(下)

    add() 含义:向集合中添加指定的元素: # 注:重复元素没办法加入. >>> s = {1,2,3} >>> s.add(2) >>> s{1 ...

  9. 用递归求n皇后问题

    此问题是指在n*n的国际象棋棋盘上 ,放置n个皇后,使得这n个皇后均不在,同一行,同一列,同一对角线上,求出合法的方案的数目. 本题可以简单转化为就是求n的全排列中的数放在棋盘上使得这几组数,符合均不 ...

  10. SpringBoot+Dubbo+Zookeeper 实例

    前言 当下Java 生态环境里面,微服务占据了非常大的份额,现在大部分新开发的 Java选型的后台程序都很奇妙的会跟微服务发生一些关系.那目前市面上主流的微服务方向主要有 Spring 家族推出的Sp ...