Arthas是Alibaba开源的Java诊断工具,深受开发者喜爱。在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。
Arthas支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的Tab自动补全功能,进一步方便进行问题的定位和诊断。
下载arthas-boot.jar,然后用java -jar的方式启动:
curl -O https://arthas.aliyun.com/arthas-boot.jar
wget https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar
java -jar arthas-boot.jar -h
如果下载速度比较慢,可以使用aliyun的镜像:
java -jar arthas-boot.jar --repo-mirror aliyun --use-http
Arthas 支持在 Linux/Unix/Mac 等平台上一键安装:
curl -L https://arthas.aliyun.com/install.sh | sh
下载启动脚本文件 as.sh 到当前目录,你可以放在任何地方或将其加入到 $PATH 中。
直接在shell下面执行./as.sh,就会进入交互界面。
也可以执行./as.sh -h来获取更多参数信息。
在Arthas的命令行界面,输入dashboard,会实时展示当前JVM应用服务的多线程状态、JVM各内存区域、GC情况等信息。
当执行trace/watch/tt/redefine等命令后,可以看到JIT线程活动变得更频繁。因为JVM热更新class字节码时清除了此class相关的JIT编译结果,需要重新编译。
JVM内部线程包括下面几种:
JIT编译线程: 如 C1 CompilerThread0, C2 CompilerThread0
GC线程: 如GC Thread0, G1 Young RemSet Sampling
其它内部线程: 如VM Periodic Task Thread, VM Thread, Service Thread
查看当前线程信息,查看线程的堆栈。
cpu使用率与linux 命令
top -H -p <pid>
的线程%CPU类似,一段采样间隔时间内,当前JVM里各个线程的增量cpu时间与采样间隔时间的比例。
输入thread会显示所有线程的状态信息
输入thread -n 3会显示当前最忙的3个线程,可以用来排查线程CPU消耗
输入thread -b 会显示当前处于BLOCKED状态的线程,可以排查线程锁的问题
java.lang.management.ThreadMXBean#getThreadCpuTime()
sun.management.HotspotThreadMBean.getInternalThreadCpuTimes()
然后睡眠等待一个间隔时间(默认为200ms,可以通过 -i 指定间隔时间)
再次第二次采样,获取所有线程的CPU时间,对比两次采样数据,计算出每个线程的增量CPU时间
线程CPU使用率 = 线程增量CPU时间 / 采样间隔时间 * 100%
注意: 这个统计也会产生一定的开销(JDK这个接口本身开销比较大),因此会看到as的线程占用一定的百分比,为了降低统计自身的开销带来的影响,可以把采样间隔拉长一些,比如5000毫秒。
没有线程ID,包含[Internal]表示为JVM内部线程,参考dashboard命令的介绍。
cpuUsage为采样间隔时间内线程的CPU使用率,与dashboard命令的数据一致。
deltaTime为采样间隔时间内线程的增量CPU时间,小于1ms时被取整显示为0ms。
time 线程运行总CPU时间。
thread -all
thread pid
thread –state <thread state>
输入jvm,查看jvm详细的性能数据
对类进行反编译:
查看JVM已加载的类信息。
“Search-Class” 的简写,这个命令能搜索出所有已经加载到 JVM 中的 Class 信息,这个命令支持的参数有 [d]、[E]、[f] 和 [x:]。
Memory Compiler/内存编译器,编译.java文件生成.class。
mc /tmp/TestSample.java
通常说一个接口性能不好,其实就是接口响应时间比较长造成的,具体代码中哪个函数耗时比较长呢?可以使用trace功能来监控一下
查找JVM中已经加载的类
$ sc -d org.springframework.web.context.support.XmlWebApplicationContext
class-info org.springframework.web.context.support.XmlWebApplicationContext
code-source /Users/xxx/work/test/WEB-INF/lib/spring-web-3.2.11.RELEASE.jar
name org.springframework.web.context.support.XmlWebApplicationContext
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name XmlWebApplicationContext
modifier public
annotation
interfaces
super-class +-org.springframework.web.context.support.AbstractRefreshableWebApplicationContext
+-org.springframework.context.support.AbstractRefreshableConfigApplicationContext
+-org.springframework.context.support.AbstractRefreshableApplicationContext
+-org.springframework.context.support.AbstractApplicationContext
+-org.springframework.core.io.DefaultResourceLoader
+-java.lang.Object
class-loader +-org.apache.catalina.loader.ParallelWebappClassLoader
+-java.net.URLClassLoader@6108b2d7
+-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@1ddf84b8
classLoaderHash 25131501
查看已加载类的方法信息
$ sm java.lang.String
java.lang.String-><init>
java.lang.String->equals
java.lang.String->toString
java.lang.String->hashCode
java.lang.String->compareTo
java.lang.String->indexOf
java.lang.String->valueOf
java.lang.String->checkBounds
java.lang.String->length
java.lang.String->isEmpty
java.lang.String->charAt
java.lang.String->codePointAt
java.lang.String->codePointBefore
java.lang.String->codePointCount
java.lang.String->offsetByCodePoints
java.lang.String->getChars
java.lang.String->getBytes
java.lang.String->contentEquals
java.lang.String->nonSyncContentEquals
java.lang.String->equalsIgnoreCase
java.lang.String->compareToIgnoreCase
java.lang.String->regionMatches
java.lang.String->startsWith
java.lang.String->endsWith
java.lang.String->indexOfSupplementary
java.lang.String->lastIndexOf
java.lang.String->lastIndexOfSupplementary
java.lang.String->substring
java.lang.String->subSequence
java.lang.String->concat
java.lang.String->replace
java.lang.String->matches
java.lang.String->contains
java.lang.String->replaceFirst
java.lang.String->replaceAll
java.lang.String->split
java.lang.String->join
java.lang.String->toLowerCase
java.lang.String->toUpperCase
java.lang.String->trim
java.lang.String->toCharArray
java.lang.String->format
java.lang.String->copyValueOf
java.lang.String->intern
查看方法 test.arthas.TestSample#execute 的调用堆栈:
$ stack test.arthas.TestSample execute
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 286 ms.
ts=2018-09-18 10:11:45;thread_name=http-bio-8080-exec-10;id=d9;is_daemon=true;priority=5;TCCL=org.apache.catalina.loader.ParallelWebappClassLoader@25131501
@test.arthas.TestSample.execute()
at javax.servlet.http.HttpServlet.service(HttpServlet.java:624)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110)
...
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:451)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1121)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
加载外部的.class文件,retransform 热更新jvm已加载的类。
retransform /tmp/TestSample.class
retransform -c 327a647b /tmp/TestSample.class /tmp/Test\$Inner.class
retransform指定的 .class 文件
$ retransform /tmp/TestSample.class
retransform success, size: 1, classes:
com.TestSample
加载指定的 .class 文件,然后解析出class name,再retransform jvm中已加载的对应的类。每加载一个 .class 文件,则会记录一个 retransform entry。
如果多次执行 retransform 加载同一个 class 文件,则会有多条 retransform entry.
$ retransform -l
Id ClassName TransformCount LoaderHash LoaderClassName
1 com.TestSample 1 null null
TransformCount 统计在 ClassFileTransformer#transform 函数里尝试返回 entry对应的 .class文件的次数,但并不表明transform一定成功。
需要指定 id:
retransform -d 1
retransform --deleteAll
显式触发 retransform
$ retransform --classPattern com.TestSample
retransform success, size: 1, classes:
com.TestSample
注意:对于同一个类,当存在多个 retransform entry时,如果显式触发 retransform ,则最后添加的entry生效(id最大的)。
如果对某个类执行 retransform 之后,想消除影响,则需要:
删除这个类对应的 retransform entry
如果不清除掉所有的 retransform entry,并重新触发 retransform ,则arthas stop时,retransform过的类仍然生效。