技术解析

Arthas 3.5.1 发布:神级特性!内存搜索对象
0
2021-06-01 10:19:01
idczone

Arthas是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。

  • Github: https://github.com/alibaba/arthas
  • 文档:https://arthas.aliyun.com/doc/

以前使用watch等命令时,我们通常要先知道哪个类,调用了哪个函数,然后触发调用。这样有局限:

  1. 线上触发调用比较难
  2. 要 watch 到正确的函数可能要选择多次
  3. 条件表达式 /结果表达式 可能需要多次测试

另外,如果想要查找内存里的对象,需要 heapdump 再分析。

Arthas 在最新发布的 3.5.1 版本里,带来神级特性:通过vmtool命令,可以在 JVM 内存搜索对象。

vmtool 在线教程

下面以vmtool在线教程为例,演示vmtool命令的功能:

  • https://arthas.aliyun.com/doc/arthas-tutorials.html?language=cn&id=command-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

查找 jvm 里的字符串对象

首先,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 参数

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

所以上面的命令实际上等值于:

vmtool --action getInstances --className java.lang.String --limit 10

如果设置--limit为负数,则遍历所有对象。

查找 spring context

以前的在线教程里,我们需要通过tt命令来拦载 spring 调用,然后获取到 spring context 。

通过vmtool命令,我们可以直接获取到 srping context:

$ vmtool --action getInstances \
--className org.springframework.context.ApplicationContext
@ApplicationContext[][
    @AnnotationConfigEmbeddedWebApplicationContext[org.springframework.boot[email protected]12028586: startup date [Thu May 13 16:08:38 UTC 2021]; root of context hierarchy],
]

指定返回结果展开层数

getInstances action 返回结果绑定到instances变量上,它是数组。

通过 -x/--expand 参数可以指定结果的展开层次,默认值是 1 。

vmtool --action getInstances --className org.springframework.context.ApplicationContext -x 2

获取 srping bean,执行表达式

getInstances action 返回结果绑定到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[
    [email protected][1],
    [email protected][name1],
]

查找所有的 spring mapping 对象

vmtool --action getInstances --className org.springframework.web.servlet.HandlerMapping

$ vmtool --action getInstances --className org.springframework.web.servlet.HandlerMapping
@HandlerMapping[][
    @SimpleUrlHandlerMapping[[email protected]5d3819c8],
    @EmptyHandlerMapping[org.springframework.web.servlet.c[email protected]11d509ba],
    @RequestMappingHandlerMapping[org.springframework[email protected]56a5f2e3],
    @WelcomePageHandlerMapping[org.springframework.boot.auto[email protected]4c0a4ed3],
    @EmptyHandlerMapping[org.springframework.web.servlet.c[email protected]51e1f8c3],
    @BeanNameUrlHandlerMapping[or[email protected]68c0a39c],
    @SimpleUrlHandlerMapping[[email protected]110b768d],
]

查找所有的 javax.servlet.Filter

在 Arthas 的在线教程里,我们介绍过怎么排查 http 请求 404/401 的问题。使用的是 trace javax.servlet.Filter *命令。

现在使用vmtool命令,我们可以直接查找出所有的 Filter 对象,加速定位过程。

$ vmtool --action getInstances --className javax.servlet.Filter
@Filter[][
    @OrderedCharacterEncodingFilter[org[email protecte抗投诉服务器d]49b69493],
    @OrderedHiddenHttpMethodFilter[or[email protected]5477cb9e],
    @AdminFilter[[email protected]],
    @WsFilter[[email protected]],
    @OrderedRequestContextFilter[[email protected]6bed550e],
    @OrderedHttpPutFormContentFilter[org.[email protected]3e538cba],
]

指定 classloader name

在多 classloader 情况下,还可以指定 classloader 来查找对象:

vmtool --action getInstances \
 --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader \
 --className org.springframework.context.ApplicationContext

指定 classloader hash

可以通过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      [email protected]2
                     [email protected]
                       [email protected]
 classLoaderHash   19469ea2

然后用-c/--classloader 参数指定:

vmtool --action getInstances \
-c 19469ea2 \
--className org.springframework.context.ApplicationContext

强制 GC

当启用 -XX:+DisableExplicitGC的 JVM 参数之后,调用System.Gc()可能并不会触发 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,参与开发
数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服