

Arthas是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。
以前使用watch等命令时,我们通常要先知道哪个类,调用了哪个函数,然后触发调用。这样有局限:
另外,如果想要查找内存里的对象,需要 heapdump 再分析。
Arthas 在最新发布的 3.5.1 版本里,带来神级特性:通过vmtool命令,可以在 JVM 内存搜索对象。
下面以vmtool在线教程为例,演示vmtool命令的功能:
首先启动任意 spring boot 应用,比如:
wget https://github.com/hengyunabc/spring-boot-inside/raw/master/demo-arthas-spring-boot/demo-arthas-spring-boot.jar java -jar demo-arthas-spring-boot.jar 然后用arthas attach 目标进程,成功之后就可以使用vmtool命令了:
wget https://arthas.aliyun.com/arthas-boot.jar java -jar arthas-boot.jar 首先,vmtool命令通过getInstances这个 action,在 JVM 里搜索字符串:
$ vmtool --action getInstances --className java.lang.String @String[][ @String[Sorry, deque too big], @String[head=%d tail=%d capacity=%d%n], @String[elements=%s%n], @String[sun/nio/ch/IOVecWrapper], @String[40252e37-8a73-4960-807e-3495addd5b08:1620922383791], @String[40252e37-8a73-4960-807e-3495addd5b08:1620922383791], @String[sun/nio/ch/AllocatedNativeObject], @String[sun/nio/ch/NativeObject], @String[sun/nio/ch/IOVecWrapper$Deallocator], @String[Java_sun_nio_ch_FileDispatcherImpl_writev0], ] 通过
--limit参数,可以限制返回值数量,避免获取超大数据时对 JVM 造成压力。默认值是 10 。
所以上面的命令实际上等值于:
vmtool --action getInstances --className java.lang.String --limit 10 如果设置--limit为负数,则遍历所有对象。
以前的在线教程里,我们需要通过tt命令来拦载 spring 调用,然后获取到 spring context 。
通过vmtool命令,我们可以直接获取到 srping context:
$ vmtool --action getInstances \ --className org.springframework.context.ApplicationContext @ApplicationContext[][ @AnnotationConfigEmbeddedWebApplicationContext[org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@12028586: startup date [Thu May 13 16:08:38 UTC 2021]; root of context hierarchy], ]
getInstancesaction 返回结果绑定到instances变量上,它是数组。
通过
-x/--expand参数可以指定结果的展开层次,默认值是 1 。
vmtool --action getInstances --className org.springframework.context.ApplicationContext -x 2
getInstancesaction 返回结果绑定到instances变量上,它是数组。可以通过--express参数执行指定的表达式。
比如,查找所有的 spring beans 名字:
vmtool --action getInstances \ --className org.springframework.context.ApplicationContext \ --express 'instances[0].getBeanDefinitionNames()' 比如,调用userController.findUserById(1)函数:
$ vmtool --action getInstances \ --className org.springframework.context.ApplicationContext \ --express 'instances[0].getBean("userController").findUserById(1)' @User[ id=@Integer[1], name=@String[name1], ] vmtool --action getInstances --className org.springframework.web.servlet.HandlerMapping
$ vmtool --action getInstances --className org.springframework.web.servlet.HandlerMapping @HandlerMapping[][ @SimpleUrlHandlerMapping[org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@5d3819c8], @EmptyHandlerMapping[org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@11d509ba], @RequestMappingHandlerMapping[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@56a5f2e3], @WelcomePageHandlerMapping[org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WelcomePageHandlerMapping@4c0a4ed3], @EmptyHandlerMapping[org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@51e1f8c3], @BeanNameUrlHandlerMapping[org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@68c0a39c], @SimpleUrlHandlerMapping[org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@110b768d], ] 在 Arthas 的在线教程里,我们介绍过怎么排查 http 请求 404/401 的问题。使用的是 trace javax.servlet.Filter *命令。
现在使用vmtool命令,我们可以直接查找出所有的 Filter 对象,加速定位过程。
$ vmtool --action getInstances --className javax.servlet.Filter @Filter[][ @OrderedCharacterEncodingFilter[org.springframework.boot.web.filter.OrderedCharacterEncodingFilter@49b69493], @OrderedHiddenHttpMethodFilter[org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter@5477cb9e], @AdminFilter[com.example.demo.arthas.AdminFilterConfig$AdminFilter@3b625385], @WsFilter[org.apache.tomcat.websocket.server.WsFilter@14875f22], @OrderedRequestContextFilter[org.springframework.boot.web.filter.OrderedRequestContextFilter@6bed550e], @OrderedHttpPutFormContentFilter[org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter@3e538cba], ] 在多 classloader 情况下,还可以指定 classloader 来查找对象:
vmtool --action getInstances \ --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader \ --className org.springframework.context.ApplicationContext 可以通过sc命令查找到加载 class 的 classloader 。
$ sc -d org.springframework.context.ApplicationContext class-info org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext code-source file:/private/tmp/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-1.5.13.RELEASE.jar!/ name org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext ... class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@19469ea2 +-sun.misc.Launcher$AppClassLoader@75b84c92 +-sun.misc.Launcher$ExtClassLoader@4f023edb classLoaderHash 19469ea2 然后用-c/--classloader 参数指定:
vmtool --action getInstances \ -c 19469ea2 \ --className org.springframework.context.ApplicationContext 当启用 -XX:+DisableExplicitGC的 JVM 参数之后,调用System.Gc()
vmtool里提供了强制 GC 的功能:
vmtool --action forceGc 如果应用配置了-verbose:gc参数,则可以在应用的标准输出里看到类似的日志:
[GC (JvmtiEnv ForceGarbageCollection) 25760K->17039K(349696K), 0.0015299 secs] [Full GC (JvmtiEnv ForceGarbageCollection) 17039K->16840K(353792K), 0.0154049 secs] vmtool功能是在社区开发者dragon-zhang(张子成)的最初 PR 上,多次讨论修改完成的,感谢他的工作,同时欢迎大家提出 PR,参与开发。上面只展示使用vmtool命令操作 spring context 的例子。实际上可以应用在很多方面,比如:
欢迎大家到 Issue 里分享使用经验: https://github.com/alibaba/arthas/issues
我们正在寻找小伙伴,特别是深圳的同学,欢迎大家加入。
1 hqs0417 2021-05-18 11:35:54 +08:00 66666 |
2 dqzcwxb 2021-05-18 11:52:34 +08:00 这个 cooooooooooooooooooool |
3 AutumnVerse 2021-05-18 11:54:30 +08:00 via Android 第一想法,这玩意拿来写外挂行吗? |
4 lululau 2021-05-18 11:58:36 +08:00 赞,要是能用 kotlin / clojure / groovy 作为 expression lang 就好了 |
5 young1lin 2021-05-18 12:19:38 +08:00 可以,很棒棒。 我试了下链接里的 demo,棒棒棒!那个招聘的要求有咩有更具体的呢,大致的方向我好专攻一下。 |
6 hengyunabc OP @lululau 有几个影响。 1. ClassLoader可能会有泄露,比如 groovy 就很多版本都有问题 2. arthas 目前要支持 jdk6 3. 这些语言体积通常比较大,使 arthas 本身体积增大 4. 会对 jvm 本身 meta space 造成压力 |
7 hengyunabc OP @young1lin 可以在公众号里加我微信,欢迎投递简历 :) |
8 Ayanokouji 2021-05-18 13:25:47 +08:00 还可以查 jvm 级别 cache |
9 lululau 2021-05-18 13:34:36 +08:00 @hengyunabc 是否可以做成可选项,比如在测试环境使用时,一般更在乎使用上是否便利,而对系统稳定性的影响在可接受的范围内用户是可以容忍或者说是忽略的 |
10 hAppyTreeFrienDs 2021-05-18 13:59:42 +08:00 6666 |
11 hengyunabc OP @lululau 没有太理解。是否使用 arthas,使用哪个命令,对用户来说本身就是自己做的选择。 |
12 v2orz 2021-05-18 14:20:58 +08:00 团队用了快两年,感谢 |