百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

Arthas,开源的 Java 诊断工具

mhr18 2024-12-26 13:10 24 浏览 0 评论

在日常开发中,当遇到线上异常情况如接口响应慢、内存飙升、CPU占用高等问题时,进行有效的监控和排查可以帮助我们快速定位和解决问题。相信大家第一时间都能想到JDK自带工具:如jconsole和jvisualvm。这些工具可以监控应用程序的性能指标、线程状态、内存使用情况等。或者还有一个最好用的工具——jprofiler,唯一遗憾的是它的高级功能是收费的。所以,我今天要推荐另一个很好用的工具Arthas。


简介

Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。

背景

通常情况下,本地开发环境无法直接访问生产环境,这使得在生产环境中进行远程调试成为不可能。而且,在生产环境中进行调试会导致服务暂停,这是不可接受的。

为了解决这个问题,开发人员可以尝试在测试环境或预发环境中复现生产环境中的问题。然而,有些问题很难在不同的环境中复现,甚至在重新启动后可能会消失。

如果您考虑在代码中添加日志来帮助解决问题,您将需要经历测试、预发和生产等多个阶段。这种方法效率低下,更糟糕的是,一旦 JVM 重新启动,问题可能无法复现,正如前文所述。

Arthas的出现旨在解决这些问题。开发人员可以在线解决生产环境中的问题,无需重新启动JVM,也无需修改代码。作为一个观察者,Arthas永远不会暂停正在运行的线程,确保服务的持续运行。

通过使用Arthas,开发人员可以实时监控应用程序的状态,查看线程堆栈信息,分析方法耗时,甚至进行代码热替换,从而快速定位和解决生产环境中的问题,提高排查和调试的效率。

Arthas能为你做什么?

  1. 查找类加载信息和解决类相关异常:Arthas可以查找某个类是从哪个jar包加载的,并提供类加载器链信息。通过Arthas,您可以追踪类加载路径,找出为什么会出现类相关的异常。
  2. 调试代码执行路径和问题定位:Arthas可以帮助您跟踪代码执行路径,查看方法调用情况,并且可以实时查看变量值。如果您发现代码没有执行到预期位置,Arthas可以帮助您分析是否分支选择错误或者代码未提交等问题。
  3. 在线调试和避免重启应用:Arthas以观察者的方式工作,不会暂停正在运行的线程,因此您可以在线上进行调试而无需重启应用或停止服务。这样您就可以避免重新发布和加日志的繁琐过程。
  4. 线上问题排查和数据处理:Arthas可以帮助您在生产环境中排查问题,即使无法重现线下环境中的问题。您可以实时监控用户数据处理过程,查看变量状态,甚至修改代码逻辑来验证问题。
  5. 全局视角查看系统状态和监控JVM:Arthas提供了全局视角来查看系统的运行状况,包括线程状态、内存使用情况、GC情况等。您可以实时监控JVM的运行状态,查找性能瓶颈和资源消耗过多的地方。
  6. 快速定位应用热点和生成火焰图:Arthas可以帮助您快速找到应用程序的热点,分析方法的耗时和调用关系,并支持生成火焰图,以便更好地了解应用程序的性能瓶颈。
  7. 在JVM内查找类的实例:Arthas提供了命令来直接从JVM内查找某个类的实例。您可以通过Arthas来检查对象状态、调用方法,甚至修改对象属性。

支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

快速入门

1、启动 arthas

# 下载arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar
# 启动
java -jar arthas-boot.jar

2、选择应用 java 进程:

上面截图是我们生产环境的所有java进程,选择一个进程,比如输入 3,再输入回车/enter。Arthas 会 attach 到目标进程上,并输出日志:

3、查看dashboard

输入dashboard,按回车/enter,会展示当前进程的信息,按ctrl+c可以中断执行。

以上就是简单的入门操作

高级手册

查看当前 JVM 信息,输入jvm,按回车/enter

  • MACHINE-NAME:机器名称,这里是"575098@bogon"。
  • JVM-START-TIME:JVM启动时间,即2024年1月22日13:04:13。
  • MANAGEMENT-SPEC-VERSION:JVM管理规范版本,这里是1.2。
  • SPEC-NAME:Java虚拟机规范的名称,即"Java Virtual Machine Specification"。
  • SPEC-VENDOR:Java虚拟机规范的供应商,这里是Oracle Corporation。
  • SPEC-VERSION:Java虚拟机规范的版本,这里是1.8。
  • VM-NAME:JVM的名称,即"Java HotSpot(TM) 64-Bit Server VM"。
  • VM-VENDOR:JVM的供应商,这里是Oracle Corporation。
  • VM-VERSION:JVM的版本,这里是25.351-b10。
  • INPUT-ARGUMENTS:JVM的输入参数,包括一些Java代理、系统属性和内存设置等。
  • CLASS-PATH:类路径,指定了JVM在加载类时查找的位置。
  • BOOT-CLASS-PATH:引导类路径,指定了JVM在启动时加载的核心库文件的位置。
  • LIBRARY-PATH:库路径,指定了本地库文件的位置

  • LOADED-CLASS-COUNT:已加载的类数量,即32742。
  • TOTAL-LOADED-CLASS-COUNT:总加载的类数量,即33220。
  • UNLOADED-CLASS-COUNT:未加载的类数量,即478。
  • IS-VERBOSE:是否启用了详细输出模式,这里是false,表示未启用。



  • NAME:编译器名称,这里是HotSpot 64-Bit Tiered Compilers,表示使用的是HotSpot虚拟机的64位分层编译器。
  • TOTAL-COMPILE-TIME:总编译时间,即47502毫秒。

  • PS Scavenge:新生代垃圾回收器(Parallel Scavenge)的统计信息。collectionCount:垃圾回收次数,即454次。collectionTime:垃圾回收所花费的时间,即2035毫秒。
  • PS MarkSweep:老年代垃圾回收器(Parallel Mark-Sweep)的统计信息。collectionCount:垃圾回收次数,即7次。collectionTime:垃圾回收所花费的时间,即1609毫秒。

  • CodeCacheManager:代码缓存管理器,用于存储已编译的代码。
  • Metaspace Manager:元空间管理器,用于存储类的元数据。Compressed Class Space:压缩类空间,用于存储压缩后的类数据。
  • PS Scavenge:新生代垃圾回收器(Parallel Scavenge)的内存管理器统计信息。PS Eden Space:伊甸园区,用于存储新创建的对象。PS Survivor Space:幸存者区,用于存储幸存的对象。
  • PS MarkSweep:老年代垃圾回收器(Parallel Mark-Sweep)的内存管理器统计信息。PS Eden Space:伊甸园区,用于存储新创建的对象。PS Survivor Space:幸存者区,用于存储幸存的对象。PS Old Gen:老年代,用于存储长时间存活的对象。

  • HEAP-MEMORY-USAGE:堆内存使用情况统计。init:初始化大小,即128.0 MiB。used:已使用大小,即171.6 MiB。committed:已分配大小,即243.5 MiB。max:最大可用大小,即243.5 MiB。
  • NO-HEAP-MEMORY-USAGE:非堆内存使用情况统计。init:初始化大小,即2.4 MiB。used:已使用大小,即267.6 MiB。committed:已分配大小,即280.8 MiB。max:最大可用大小,即无限制。

  • OS:操作系统名称,该值为 "Linux"。
  • ARCH:操作系统架构,该值为 "amd64",表示64位系统。
  • PROCESSORS-COUNT:处理器数量,该值为 2,表示有2个处理器核心。
  • LOAD-AVERAGE:系统平均负载,该值为 2.35,表示系统负载相对较高。
  • VERSION:操作系统版本,该值为 "4.18.0-372.9.1.el8.x86_64"。

  • COUNT:线程数量,该值为 97,表示当前JVM存在 97 个线程。
  • DAEMON-COUNT:守护线程数量,该值为 78,表示当前JVM中有 78 个守护线程。
  • PEAK-COUNT:线程数量峰值,该值为 98,表示JVM曾经达到最高峰时有 98 个线程。
  • STARTED-COUNT:已启动线程数量,该值为 1347,表示JVM自启动以来已启动了 1347 个线程。
  • DEADLOCK-COUNT:死锁线程数量,该值为 0,表示当前JVM中没有发生死锁。如果 DEADLOCK-COUNT 的值大于 0,表示系统中存在死锁问题,需要进行处理。

查看当前内存占用情况,输入memory,按回车/enter

  • heap:堆内存统计信息。
  • ps_eden_space:伊甸园区(Eden Space)内存统计信息。
  • ps_survivor_space:幸存者区(Survivor Space)内存统计信息。
  • ps_old_gen:老年代(Old Gen)内存统计信息。
  • nonheap:非堆内存统计信息。
  • code_cache:代码缓存统计信息。
  • metaspace:元空间(Metaspace)统计信息。
  • compressed_class_space:压缩类空间统计信息。
  • direct:直接内存统计信息。
  • mapped:映射内存统计信息。

查看当前最忙的前n 个线程并打印堆栈

语法:thread -n 次数

当没有参数时,显示一页线程的信息,默认按照 CPU 增量时间降序排列,只显示第一页数据。

  • ID:线程的唯一标识符。
  • NAME:线程的名称。
  • GROUP:线程所属的线程组。
  • PRIORITY:线程的优先级,范围为 1~10,数值越大表示优先级越高。
  • STATE:线程的状态,包括 NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(计时等待)和 TERMINATED(终止)。
  • %CPU:线程在当前 CPU 核心上的 CPU 占用率,取值范围为 0~100。
  • DELTA_TIME:线程最近一次获取 CPU 时间片的时间间隔。
  • TIME:线程已经运行的总时间。
  • INTERRUPTED:线程是否被中断过。
  • DAEMON:线程是否是守护线程。

thread -b 找出当前阻塞其他线程的线程

有时候我们发现应用卡住了, 通常是由于某个线程拿住了某个锁, 并且其他线程都在等待这把锁造成的。 为了排查这类问题, arthas 提供了thread -b, 一键找出那个罪魁祸首。

函数执行数据观测,watch 的参数比较多,我用一个controller接口请求类来做范例

假如controller类的伪代码是这个样子

//包名
package com.**.controller.auth;

@RequestMapping("/auth")
//类名
public class AuthController  {
		   
    @PostMapping("/login")
   //方法名
    public ApiResult<AdminLoginRespVO> adminLogin(@Valid @RequestBody AdminLoginReqVO reqVO) {
    		//其他密码校验逻辑
    }
}
  1. 观察函数调用返回时的参数、this 对象和返回值

语法:watch 类名表达式匹配 方法名表达式匹配 观察表达式

观察表达式,默认值:{params, target, returnObj}

下图,我没写观察表达式,默认就是{params, target, returnObj}

  • 上面的结果里,说明函数被执行了两次,第一次结果是location=AtExceptionExit,说明函数抛出异常了,因此returnObj是 null
  • 在第二次结果里是location=AtExit,说明函数正常返回,因此可以看到returnObj结果是com.homgar.module.common.vo.ApiResult@3474ee64 对象了。
  1. 观察函数调用入口的参数和返回值

语法:watch 类名表达式匹配 方法名表达式匹配 "{params,returnObj}"

  1. 同时观察函数并指定调用次数后退出观察

语法:watch 类名表达式匹配 方法名表达式匹配 "{params,returnObj}" -n 次数

反编译class文件

语法:jad --source-only 类全路径

有的时候,由于本地代码已经被改动了,或者不确定生产环境的代码。可以考虑用这个方法查看源文件,具体定位问题代码。

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

语法:trace 类名表达式匹配 方法名表达式匹配

方法执行监控

语法:monitor 类名表达式匹配 方法名表达式匹配

用于监控某个方法,没间隔时间内执行的次数,一般用于统计。

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

通过 --limit参数,可以限制返回值数量,避免获取超大数据时对 JVM 造成压力。默认值是 10。

  • 查看spring所管理的所有的bean名称
  • 查看具体的某个bean及其属性
vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBean("authController")' -x 3
  • 调用bean的方法
vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBean("userController").get(11111)'


  • 强制 GC
vmtool --action forceGc

修复热部署

一般很少使用,除非特殊情况,用于无法发布版本,临时修复某个已经的bug

使用 jad 命令反编译 AuthController 类并保存为 Java 源文件:

jad --source-only com.XXX.controller.AuthController > /tmp/AuthController.java

使用 sc 命令查找加载 AuthController 类的类加载器的哈希值:

sc -d *AuthController | grep classLoader

在输出结果中,找到类加载器的哈希值,比如 21b8d17c。再使用 mc 命令使用指定的类加载器重新编译 AuthController 类:

mc -c 21b8d17c /tmp/AuthController.java -d /tmp

使用 redefine 命令将重新编译后的类加载到 JVM 中:

redefine /tmp/com/XXX/controller/AuthController.class

讲到这里,Arthas常用的命令以及用法,基本上都有介绍了,更多详细的内容,我们可以参考官方文档。

最后感谢友友的浏览,如文章有错误的地方或者更好的方法,欢迎一起评论区交流。[给你小心心][给你小心心][给你小心心]

相关推荐

MySQL数据库中,数据量越来越大,有什么具体的优化方案么?

个人的观点,这种大表的优化,不一定上来就要分库分表,因为表一旦被拆分,开发、运维的复杂度会直线上升,而大多数公司和开发人员是欠缺这种能力的。所以MySQL中几百万甚至小几千万的表,先考虑做单表的优化。...

Redis的Bitmap(位图):签到打卡、用户在线状态,用它一目了然

你是不是每天打开APP,第一时间就是去“签到打卡”?或者在社交软件里,看到你的朋友头像旁边亮着“在线”的绿灯?这些看似简单的功能背后,都隐藏着一个有趣而高效的数据结构。如果让你来设计一个签到系统:用户...

想知道有多少人看了你的文章?Redis HyperLogLog几KB就搞定!

作为一名内容创作者,你每天最期待的,除了文章阅读量蹭蹭上涨,是不是还特别想知道,到底有多少个“独立用户”阅读了你的文章?这个数字,我们通常称为“UV”(UniqueVisitors),它比总阅读量更...

Redis的“HyperLogLog”:统计网站日活用户,省内存又高效的神器

你可能从未听过这个拗口的名字——“HyperLogLog”,它听起来就像是某个高深莫测的数学公式。但请相信我,理解它的核心思想并不难,而且一旦你掌握了它,你会发现它在处理大数据统计问题时,简直就是“救...

阿里云国际站:为什么我的云服务器运行缓慢?

本文由【云老大】TG@yunlaoda360撰写一、网络性能瓶颈带宽不足现象:上传/下载速度慢,远程连接卡顿。排查:通过阿里云控制台查看网络流量峰值是否接近带宽上限34。解决:升级带宽(如从1M提...

Java 近期新闻:Jakarta EE 11和Spring AI更新、WildFly 36.0 Beta、Infinispan

作者|MichaelRedlich译者|明知山策划|丁晓昀OpenJDKJEP503(移除32位x86移植版本)已从“ProposedtoTarget”状态进入到“T...

腾讯云国际站:怎样设置自动伸缩应对流量高峰?

云计算平台服务以阿里云为例:开通服务与创建伸缩组:登录阿里云控制台,找到弹性伸缩服务并开通。创建伸缩组时,选择地域与可用区,定义伸缩组内最小/最大实例数,绑定已有VPC虚拟交换机。实例模板需...

【案例分享】如何利用京东云建设高可用业务架构

本文以2022年一个实际项目为基础,来演示在京东云上构建高可用业务的整个过程。公有云及私有云客户可通过使用京东云的弹性IAAS、PAAS服务,创建高可用、高弹性、高可扩展、高安全的云上业务环境,提升业...

Spring Security在前后端分离项目中的使用

1文章导读SpringSecurity是Spring家族中的一个安全管理框架,可以和SpringBoot项目很方便的集成。SpringSecurity框架的两大核心功能:认证和授权认证:...

Redis与Java集成的最佳实践

Redis与Java集成的最佳实践在当今互联网飞速发展的时代,缓存技术的重要性毋庸置疑。Redis作为一款高性能的分布式缓存数据库,与Java语言的结合更是如虎添翼。今天,我们就来聊聊Redis与Ja...

Redis在Java项目中的应用与数据持久化

Redis在Java项目中的应用与数据持久化Redis简介:为什么我们需要它?在Java项目中,Redis就像一位不知疲倦的快跑选手,总能在关键时刻挺身而出。作为一个内存数据库,它在处理高并发请求时表...

Redis 集群最大节点个数是多少?

Redis集群最大节点个数取决于Redis的哈希槽数量,因为每个节点可以负责多个哈希槽。在Redis3.0之前,Redis集群最多支持16384个哈希槽,因此最大节点数为16384个。但是在Redi...

Java开发岗面试宝典:分布式相关问答详解

今天千锋广州Java小编就给大家分享一些就业面试宝典之分布式相关问题,一起来看看吧!1.Redis和Memcache的区别?1、存储方式Memecache把数据全部存在内存之中,断电后会挂掉,数据不...

当Redis内存不足时,除了加内存,还有哪些曲线救国的办法?

作为“速度之王”的Redis,其高性能的秘密武器之一就是将数据存储在内存中。然而,内存资源是有限且昂贵的。当你的Redis实例开始告警“内存不足”,或者写入请求被阻塞时,最直接的解决方案似乎就是“加内...

商品详情页那么多信息,Redis的“哈希”如何优雅存储?

你每天网购时,无论是打开淘宝、京东还是拼多多,看到的商品详情页都琳琅满目:商品名称、价格、库存、图片、描述、评价数量、销量。这些信息加起来,多的惊人。那么问题来了:这些海量的商品信息,程序是去哪里取出...

取消回复欢迎 发表评论: