本文将结合具体案例,为大家介绍Arthas的常用功能与命令。
一、启动Arthas
Arthas支持在线实例附加,也支持离线命令行启动。这里我们选择在线实例附加:
-
下载arthas-boot.jar
wget https://alibaba.github.io/arthas/arthas-boot.jar
然后使用java -jar执行:
java -jar arthas-boot.jar
- 选择目标Java进程,按Enter确认附加:
* [1]: 6492 org.jetbrains.jps.cmdline.Launcher
* [2]: 21567
* [3]: 1304 org.apache.catalina.startup.Bootstrap start
* [4]: 22481
选择要附加的Java进程:3
- 就这样,Arthas成功附加到我们的应用中,并打开interactive shell和dashboard
二、查看JVM信息
启动Arthas后,我们首先要熟悉的就是查看JVM信息的相关命令:
dashboard
命令
可以用于展示当前应用程序的实时性能数据和运行状况摘要
dashboard
运行该命令后,会在终端上打印出类似下面这样的实时性能数据和运行状况信息:
date/time | cpu % | mem usage | req/s | 2xx/s | 3xx/s | 4xx/s | 5xx/s | rt(ms) | status
2023-04-23 09:40:00 | 1.71 | 93.15% | 0 | 0 | 0 | 0 | 0 | 0.18 | run
2023-04-23 09:41:00 | 0.98 | 93.15% | 0 | 0 | 0 | 0 | 0 | 0.13 | run
2023-04-23 09:42:00 | 1.34 | 93.16% | 0 | 0 | 0 | 0 | 0 | 0.17 | run
2023-04-23 09:43:00 | 1.01 | 93.16% | 0 | 0 | 0 | 0 | 0 | 0.14 | run
2023-04-23 09:44:00 | 1.17 | 93.16% | 0 | 0 | 0 | 0 | 0 | 0.17 | run
2023-04-23 09:45:00 | 1.28 | 93.15% | 0 | 0 | 0 | 0 | 0 | 0.16 | run
上面的输出中,各列含义如下:
-
date/time
:时间戳,格式为YYYY-MM-DD HH:MM:SS
; -
cpu %
:CPU 使用率百分比; -
mem usage
:内存使用率百分比; -
req/s
:每秒处理请求数; -
2xx/s
:每秒成功响应 2xx 状态码的请求数; -
3xx/s
:每秒成功响应 3xx 状态码的请求数; -
4xx/s
:每秒成功响应 4xx 状态码的请求数; -
5xx/s
:每秒成功响应 5xx 状态码的请求数; -
rt(ms)
:平均响应时间(毫秒); -
status
:当前应用程序运行状态。
其中 status
列有以下几种可能的取值:
-
run
:应用程序正在运行; -
down
:应用程序已经停止。
此外,dashboard
命令还会在终端上显示以下统计信息:
-
cpu
:当前应用程序 CPU 的使用情况; -
memory
:当前应用程序内存的使用情况; -
thread
:当前应用程序线程池的使用情况; -
gc
:当前应用程序垃圾回收的情况。
这些统计信息将占用屏幕上的一部分空间,并且每隔一段时间就会更新一次。
除了以上列出的信息之外,dashboard
命令还允许你使用键盘的方向键来滚动屏幕,以查看更早之前的数据。同时,你也可以使用键盘上的 q
键来退出 dashboard
视图
thread 命令
thread
命令可以用于展示应用程序中所有线程的当前状态信息。以下是 thread
命令的正确用法:
thread
运行该命令后,会在终端上打印出类似下面这样的所有线程的状态信息:
"Attach Listener" daemon prio=9 tid=0x7f57bc000800 nid=0x116f waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"pool-1-thread-1" prio=5 tid=0x7f57c006c000 nid=0x1098 runnable [0x7f57d32e3000]
java.lang.Thread.State: RUNNABLE
at com.alibaba.arthas.samples.thread.ThreadSample$MyThread.run(ThreadSample.java:25)
"main" prio=5 tid=0x7f57bc000800 nid=0x1054 in Object.wait() [0x7f57d3fff000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.alibaba.arthas.samples.thread.ThreadSample.main(ThreadSample.java:10)
上面的输出中,每个线程的状态信息包括了以下几个部分:
- 线程名称(用双引号括起来);
- 线程调度优先级(prio);
- 线程 ID (tid);
- 线程进程内唯一标识符(nid);
- 等待条件(waiting on condition),如果线程正在等待某个条件,则会显示该条件;
- 线程当前所处状态(Thread.State);
- 如果是 Java 应用程序线程,还会显示线程当前执行的方法和源代码行号。
其中,Thread.State
列是最为重要的一列,它表示了线程当前的状态。Java 线程有以下几种状态:
-
NEW
:尚未启动的线程的状态。 -
RUNNABLE
:正在执行中的线程的状态。 -
BLOCKED
:因为某些原因无法继续执行的线程的状态(例如等待获取某个锁)。 -
WAITING
:因为某些条件的发生而无法继续执行的线程的状态。 -
TIMED_WAITING
:与WAITING
状态类似,但是可以设置超时时间,在时间到达之前无法继续执行的线程的状态。 -
TERMINATED
:已经执行完毕的线程的状态。
除了以上列出的信息之外,thread
命令还允许你使用键盘的方向键来滚动屏幕,以查看更多线程。同时,你也可以使用键盘上的 q
键来退出 thread
视图。
JVM命令
jvm
命令可以帮助你查看和管理当前应用程序的 JVM 状态和配置参数。以下是 jvm
命令的正确用法:
jvm
运行该命令后,会在终端上打印出如下所示的 JVM 相关信息:
PID: 1234
Java Home: /usr/local/java/jdk1.8.0_131/jre
Java Version: 1.8.0_131
Java VM Name: Java HotSpot(TM) 64-Bit Server VM
Java VM Vendor: Oracle Corporation
Java Options: -XX:+HeapDumpOnOutOfMemoryError -Xmx512m -Xms256m
上面的输出中,各列含义如下:
-
PID
:当前应用程序的进程 ID; -
Java Home
:当前应用程序使用的 JDK/JRE 的安装路径; -
Java Version
:当前应用程序所使用的 JDK/JRE 版本号; -
Java VM Name
:JVM 实现商名称; -
Java VM Vendor
:JVM 实现商名称; -
Java Options
:当前应用程序启动时设置的 JVM 参数。
其中,Java Options
列显示了当前应用程序启动时指定的一些 JVM 参数。这些参数通常指定了 JVM 堆大小, GC 策略,堆外内存大小,日志级别等等。你可以根据实际需要调整这些参数来优化应用程序的性能和资源利用率。
除了以上列出的信息之外,jvm
命令还支持一些子命令,例如 monitor
、dump
、print
等等,用于监控 JVM 运行状况、生成堆转储文件、打印内存映射信息等等。你可以在 jvm
命令后添加这些子命令来执行相应的操作。
sysprop命令
sysprop
命令可以用于查看 Java 系统属性。以下是 sysprop
命令的正确用法:
sysprop
运行该命令后,会在终端上打印出所有 Java 系统属性及其对应的值,类似下面这样:
java.io.tmpdir = /tmp
java.class.path = /usr/local/tomcat/lib/servlet-api.jar:/usr/local/tomcat/lib/jsp-api.jar:/usr/local/tomcat/lib/tomcat-juli.jar
java.home = /usr/local/java/jdk1.8.0_131/jre
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
...
上面的输出中,每个 Java 系统属性和其对应的值都表示为一行输出,形如 property_name = property_value
。
Java 系统属性是一些由 JVM 预定义的变量,它们通常用于控制 JVM 和应用程序的行为。例如,java.home
属性表示当前使用的 JRE/JDK 安装路径,java.io.tmpdir
属性表示临时文件目录位置,java.class.path
属性表示类路径等等。你可以根据实际需要查询这些属性的值,并根据情况调整应用程序或 JVM 的配置参数,以达到更好的性能或功能需求。
除了以上列出的信息之外,sysprop
命令还支持一些选项,例如 -p
选项可以用于查找具有指定前缀的系统属性,-e
选项可以用于查找具有指定后缀的系统属性等等。你可以在命令行中加上相应的选项来执行更复杂的查询操作。
sysenv命令
sysenv
命令可以用于查看操作系统环境变量。以下是 sysenv
命令的正确用法:
sysenv
运行该命令后,会在终端上打印出所有环境变量及其对应的值,类似下面这样:
TERM_PROGRAM=Apple_Terminal
SHELL=/bin/bash
TERM=xterm-256color
TMPDIR=/var/folders/67/0__r6w1d6vq3s7s48hrtj1200000gn/T/
....
上面的输出中,每个环境变量和其对应的值都表示为一行输出,形如 variable_name=value
。
操作系统环境变量是一些由操作系统预定义的变量,它们通常用于控制操作系统和应用程序的行为。例如,PATH
环境变量表示可执行程序的搜索路径,HOME
环境变量表示当前用户的主目录路径等等。你可以根据实际需要查询这些环境变量的值,并根据情况调整应用程序或操作系统的配置参数,以达到更好的性能或功能需求。
除了以上列出的信息之外,sysenv
命令还支持一些选项,例如 -p
选项可以用于查找具有指定前缀的环境变量,-e
选项可以用于查找具有指定后缀的环境变量等等。你可以在命令行中加上相应的选项来执行更复杂的查询操作。
三、检测线程问题
thread -b 命令
thread -b
命令可以用于展示当前应用程序中所有线程的堆栈信息,并以火焰图的形式进行显示。以下是 thread -b
命令的正确用法:
thread -b
运行该命令后,会在终端上打印出如下所示的火焰图:
main
|-com.example.demo.MyController.index(MyController.java:11)
| |-com.example.demo.MyService.process(MyService.java:26)
| | |-com.example.demo.MyDao.query(MyDao.java:32)
| | | |-java.sql.PreparedStatement.executeQuery(PreparedStatement.java:-1)
| | | | |-org.apache.tomcat.jdbc.pool.interceptor.StatementDecorator$StatementProxy.executeQuery(StatementDecorator.java:212)
| | | | |-org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:63)
| | | | | |-org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)
| | | | | | |-org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:63)
| | | | | | | |-org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:324)
| | | | | | | |-org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
| | | | | | | | |-org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
| | | | | | | | |-org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:73)
| | | | | | | | |-org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:324)
| | | | | | | |-com.example.demo.MyMapper.query(MyMapper.java:38)
| | |-java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
| | |-java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
| | |-java.lang.Thread.run(Thread.java:748)
| |-sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
| |-sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
| |-sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:92)
上面的输出中,每个线程的堆栈信息都被转化为火焰图的形式进行显示。在火焰图中,每一行表示一个调用栈帧,每个矩形表示一个方法调用,矩形的宽度表示该方法在整个堆栈中的占比,颜色越深表示该方法的执行时间越长。
通过观察火焰图,你可以快速定位到代码中执行时间最长的部分,并针对这些部分进行优化和调整。例如,上面的火焰图中可以看出 MyDao.query
方法所占用的时间较长,你可以针对这个方法进行性能优化。
除了以上列出的信息之外,thread -b
命令还允许你使用键盘的方向键来滚动屏幕,以查看更早之前的堆栈信息。同时,你也可以使用键盘上的 q
键来退出火焰图视图。
thread -n 命令
thread -n
命令可以用于展示当前应用程序中所有线程的简要信息,包括线程 ID、状态、名称等。以下是 thread -n
命令的正确用法:
thread -n
运行该命令后,会在终端上打印出如下所示的线程列表:
ID STATE NAME
1 RUNNABLE main
2 WAITING Thread-1
3 TIMED_WAITING Thread-2
4 BLOCKED Thread-3
5 RUNNABLE Thread-4
6 TERMINATED Thread-5
...
上面的输出中,每行表示一个线程的简要信息,分别包括线程 ID、状态和名称等。其中,各列的含义如下:
-
ID
:线程 ID; -
STATE
:线程状态; -
NAME
:线程名称。
线程状态有多种可能,包括 RUNNABLE
(可运行状态)、WAITING
(等待状态)、TIMED_WAITING
(超时等待状态)和 BLOCKED
(阻塞状态)等。通过查看线程状态,你可以了解到线程当前的执行情况和所处状态,以便进一步进行性能和调试优化。
除了以上列出的信息之外,thread -n
命令还支持一些选项,例如 -p
选项可以用于只显示指定线程 ID 的信息,-s
选项可以用于按照指定的列排序线程列表等等。你可以在命令行中加上相应的选项来执行更复杂的操作。
trace 命令
以下是使用trace
命令监控方法调用并输出方法执行时的信息。
假设我们要监控一个Spring Boot应用中的某个Controller方法,可以执行以下命令:
trace com.example.controller.UserController getUserList
这个命令的含义是:追踪com.example.controller.UserController
类中名为getUserList
的方法,并在每次该方法被调用时输出一些运行时的信息。
当我们访问该Controller的/user/list
接口时,Arthas会自动拦截该方法的调用,并输出类似下面的结果:
Press Ctrl+C to abort.
ts=2023-04-23 17:44:42; thread_name=http-nio-8080-exec-1; [cost=4ms]com.example.controller.UserController.getUserList();
public java.util.List<com.example.entity.User> com.example.controller.UserController.getUserList() throws java.lang.Exception : 0ms
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:566)
...(省略部分输出)
这个输出表示该方法最后一次被调用的时间(timestamp)、调用线程名(thread_name)、方法的执行耗时(cost)以及方法执行的详细调用栈。其中cost
表示方法执行的耗时,单位是毫秒。
需要注意的是,trace
命令会对目标方法进行动态修改,因此可能会带来一些性能损失。在生产环境中不建议频繁使用该命令,以免影响应用程序的性能。
stack命令
stack
命令可以用来查看指定 Java 进程中某个线程的调用栈信息。
命令格式为:
stack [options] <thread-id>
其中 <thread-id>
是需要查看调用栈的线程的 ID。
可以使用的选项包括:
-
-n <num>
:指定要显示的栈帧数,默认为10。 -
-d
:输出详细的调试信息。 -
--exclude
:指定要排除的包或类名,多个用逗号分隔。 -
--exclude-re
:指定要排除的正则表达式,对应的包或类名将被排除。
例如,如果我想查看当前进程中 ID 为 1 的线程的调用栈信息,可以执行以下命令:
$ stack 1
执行结果会输出该线程的调用栈信息,每行信息包含以下几个部分:
-
at
:表示该栈帧所在的方法调用链位置。 -
<class>.<method>(<args>)
:表示该栈帧所在的方法的名称和参数列表。 -
Native Method
或者Unknown Source
:表示该栈帧所在方法的源码位置。如果是 Native Method,则表明该方法是由本地代码实现的;如果是 Unknown Source,则表明该方法没有源码信息可用。 -
line: <line-number>
:表示该栈帧所在的源码行号。如果该方法没有源码信息,则忽略该部分。
下面是一个示例输出:
$ stack 1
Thread 1:
at com.example.demo.DemoController.handleRequest(DemoController.java:13)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
...
这里输出了线程 1 的调用栈信息,包括每个栈帧所在方法的名称和参数列表,以及源码位置(如果有)。注意,第一行 "Thread 1:" 是为了区分不同线程的输出,在只查看单个线程时可能会省略
jad 命令
jad
命令可以将指定类反编译为 Java 源代码,并输出到控制台。它的用法如下:
jad 类名
其中 类名
参数指定需要反编译的类的全限定名。
例如,我们想要反编译 Spring Framework 的 org.springframework.web.servlet.DispatcherServlet
类,可以使用以下命令:
jad org.springframework.web.servlet.DispatcherServlet
执行上述命令后,Arthas 会输出该类的反编译结果,包括类的定义、成员变量和方法等信息。输出的内容非常详细,包括每个方法的访问修饰符、返回值类型、参数列表、异常抛出列表以及方法体等信息。以下是可能的输出示例:
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@1f96302a
Location:
+-/Library/Tomcat/apache-tomcat-9.0.27/webapps/demo/WEB-INF/lib/spring-webmvc-5.3.8.jar
-!org.springframework.web.servlet.DispatcherServlet
Decompiled with Fernflower-Jetbrains decompiler.
// package org.springframework.web.servlet;
public class DispatcherServlet extends FrameworkServlet {
// ...
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
}
// ...
}
在这个输出中,我们可以看到该类所处的 ClassLoader 和所在的 JAR 包路径,以及通过 Fernflower 反编译器反编译得到的类定义和方法实现。其中,类定义部分被注释为 // package org.springframework.web.servlet;
,方法部分则包括了每个方法的代码实现。
四、性能监控与调优
monitor命令
monitor
命令可以监视指定方法的调用情况,并输出相关统计信息。它的用法如下:
monitor [options] className methodName
其中,className
参数指定要监视的类的全限定名,methodName
参数指定要监视的方法名。options
参数是可选的,可以用于控制监视器的行为。
以下是一些常见的 monitor
命令选项:
-
-c
: 统计调用次数。 -
-n
: 指定最大统计次数。 -
-t
: 指定统计时间间隔,单位是毫秒。 -
-b
: 打印调用堆栈跟踪信息。
例如,我们想要监视 Spring Framework 的 org.springframework.web.servlet.DispatcherServlet
类中的 doDispatch
方法的调用情况,可以使用以下命令:
monitor -c 10 -t 1000 -b org.springframework.web.servlet.DispatcherServlet doDispatch
执行上述命令后,Arthas 会输出该方法的调用情况统计信息,包括调用次数、平均响应时间、最小响应时间、最大响应时间等指标。如果指定了 -b
选项,则还会打印每次调用的调用堆栈跟踪信息。
以下是可能的输出示例:
Monitoring org.springframework.web.servlet.DispatcherServlet.doDispatch(int,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) for 10 times, interval: 1000ms...
Press Ctrl+C to stop
Started after 1ms. Cost per invocations: AVG(0ms), MIN(0ms), MAX(0ms).
Count: 10 Average: 2ms Min: 1ms Max: 4ms
Thread Count: 1 Thread Waiting Count: 0 Total Time: 22ms
Stack Trace:
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1057)
...
Stopped after 11078ms, success rate: 100.00%
在这个输出中,我们可以看到监视器的基本信息,包括监视的方法名、监视次数和统计时间间隔等。下面是具体的统计信息:
-
Count
: 方法调用次数。 -
Average
: 平均响应时间。 -
Min
: 最小响应时间。 -
Max
: 最大响应时间。
此外,我们还可以看到线程数量和等待线程数量的统计信息,以及总共的执行时间。最后,如果指定了 -b
选项,则会在输出末尾打印每次调用的调用堆栈跟踪信息
watch 命令
watch
命令可以监视指定表达式的值,并在其发生变化时输出相关信息。它的用法如下:
watch [options] expression
其中,expression
参数是需要监视的表达式。options
参数是可选的,可以用于控制监视器的行为。
以下是一些常见的 watch
命令选项:
-
-x
: 将监视器设为一次性的,即只监视一次变化。 -
-n
: 指定最大监视次数。
例如,我们想要监视变量 i
的值,在其改变时输出信息,可以使用以下命令:
watch i
执行上述命令后,Arthas 会在变量 i
的值发生变化时输出相关信息。
以下是可能的输出示例:
2021-08-09 21:18:30
Watching i
Press Ctrl+C to abort.
[Cost ] : 3.794 ms
[Thread ] : main
[Location] : jademo.demo.MainController.lambda$watch$0(MainController.java:23)
i: 10 -> 11
2021-08-09 21:18:31
Watching i
Press Ctrl+C to abort.
[Cost ] : 1.192 ms
[Thread ] : main
[Location] : jademo.demo.MainController.lambda$watch$0(MainController.java:23)
i: 11 -> 12
在这个输出中,我们可以看到监视器每秒钟检查一次变量 i
的值,并在其发生变化时输出相关信息。输出中包括时间戳、监视的表达式、执行时间、当前线程、触发位置和变量的旧值和新值等信息。由于是连续输出的,所以可以实时观察变量值的变化情况。
heapdump 命令
heapdump
命令可以生成 Java 应用程序的内存快照,并将其保存到指定文件中。它的用法如下:
heapdump <file>
其中,file
参数是要保存内存快照的文件路径。
例如,我们想要生成一个名为 mydump.hprof
的内存快照文件,可以使用以下命令:
heapdump /path/to/mydump.hprof
执行上述命令后,Arthas 会在后台线程中生成并保存内存快照,不会阻塞当前线程。生成过程可能需要一些时间,具体时间取决于 JVM 进程的内存大小和负载情况。生成完毕后,Arthas 会输出相关提示信息。
以下是可能的输出示例:
Dumping heap to /path/to/mydump.hprof...
Heap dump file created
在这个输出中,我们可以看到提示信息,表示内存快照已经成功生成。根据应用程序的实际情况和 JVM 进程的内存大小,生成的内存快照文件可能很大,通常几百 MB 到几 GB 不等。我们可以使用工具如 Eclipse Memory Analyzer 或 jhat 等来分析内存快照文件,以了解应用程序的内存使用情况。
配合 jhat 生成与解析堆快照定位内存问题
redefine命令
redefine
命令可以动态替换 Java 应用程序中的类定义,并在不重启 JVM 的情况下生效。它的用法如下:
redefine <class-pattern> <jad-file>
其中,class-pattern
参数是需要重定义的类名模式,支持通配符 *
和正则表达式;jad-file
参数是包含新类定义的 Java 源代码文件路径或者已编译的 Class 文件路径。
例如,我们想要将 Spring Framework 中的 org.springframework.web.servlet.DispatcherServlet
类进行修改,并且能够即时生效,可以使用以下命令:
- 使用
jad
命令反编译该类:
jad org.springframework.web.servlet.DispatcherServlet
- 修改反编译得到的源代码,并将其保存为新的 Java 源代码文件
MyDispatcherServlet.java
。 - 使用
javac
命令编译源代码文件,生成对应的 Class 文件:
javac MyDispatcherServlet.java
- 使用
redefine
命令动态替换原来的类定义:
redefine org.springframework.web.servlet.DispatcherServlet /path/to/MyDispatcherServlet.class
执行上述命令后,Arthas 会重新加载指定类的新定义,并使其生效。由于此操作可能会破坏应用程序的正确性,因此建议谨慎使用。
需要注意的是,redefine
命令只能替换没有被虚拟机加载的类,也就是说,如果某个类已经被加载了,则需要先卸载它,才能重新加载新的定义。另外,在 JDK 9 及以上版本中,由于引入了模块化系统,使用 redefine
命令可能会受到一定的限制。
总结
通过上述简单的功能介绍,相信您对Arthas的强大功能已有初步了解。它可以应对我们日常开发与运维过程中绝大部分的问题,而无需重启服务。我将在后续的系列文章中,结合具体的案例来为大家详细地讲解Arthas的各项功能与命令,学会运用Arthas解决各种Java应用问题