横云断岭
2021-05-186,886 阅读 3 分钟专栏: Arthas 诊断实践
Arthas
是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。
以前使用watch
等命令时,我们通常要先知道哪个类,调用了哪个函数,然后触发调用。这样有局限:
线上触发调用比较难
要 watch 到正确的函数可能要选择多次
条件表达式/结果表达式 可能需要多次测试
另外,如果想要查找内存里的对象,需要 heapdump 再分析。
Arthas 在最新发布的 3.5.1 版本里,带来神级特性:通过vmtool
命令,可以在 JVM 内存搜索对象。
vmtool 在线教程
下面以vmtool
在线教程为例,演示vmtool
命令的功能:
首先启动任意 spring boot 应用,比如:
bash复制代码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
命令了:
arduino复制代码wget https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
复制代码
查找 jvm 里的字符串对象
首先,vmtool
命令通过getInstances
这个 action,在 JVM 里搜索字符串:
bash复制代码$ 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。
所以上面的命令实际上等值于:
bash复制代码vmtool --action getInstances --className java.lang.String --limit 10
复制代码
如果设置--limit
为负数,则遍历所有对象。
查找 spring context
以前的在线教程里,我们需要通过tt
命令来拦载 spring 调用,然后获取到 spring context。
通过vmtool
命令,我们可以直接获取到 srping context:
sql复制代码$ 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],
]
复制代码
指定返回结果展开层数
getInstances
action 返回结果绑定到instances
变量上,它是数组。
通过 -x
/--expand
参数可以指定结果的展开层次,默认值是 1。
vmtool --action getInstances --className org.springframework.context.ApplicationContext -x 2
获取 srping bean,执行表达式
getInstances
action 返回结果绑定到instances
变量上,它是数组。可以通过--express
参数执行指定的表达式。
比如,查找所有的 spring beans 名字:
scss复制代码vmtool --action getInstances \
--className org.springframework.context.ApplicationContext \
--express 'instances[0].getBeanDefinitionNames()'
复制代码
比如,调用userController.findUserById(1)
函数:
scss复制代码$ vmtool --action getInstances \
--className org.springframework.context.ApplicationContext \
--express 'instances[0].getBean("userController").findUserById(1)'
@User[
id=@Integer[1],
name=@String[name1],
]
复制代码
查找所有的 spring mapping 对象
vmtool --action getInstances --className org.springframework.web.servlet.HandlerMapping
bash复制代码$ 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],
]
复制代码
查找所有的 javax.servlet.Filter
在 Arthas 的在线教程里,我们介绍过怎么排查 http 请求 404/401 的问题。使用的是 trace javax.servlet.Filter *
命令。
现在使用vmtool
命令,我们可以直接查找出所有的 Filter 对象,加速定位过程。
bash复制代码$ 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 name
在多 classloader 情况下,还可以指定 classloader 来查找对象:
css复制代码vmtool --action getInstances \
--classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader \
--className org.springframework.context.ApplicationContext
复制代码
指定 classloader hash
可以通过sc
命令查找到加载 class 的 classloader。
bash复制代码$ 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
参数指定:
bash复制代码vmtool --action getInstances \
-c 19469ea2 \
--className org.springframework.context.ApplicationContext
复制代码
强制 GC
当启用 -XX:+DisableExplicitGC
的 JVM 参数之后,调用System.Gc()
可能并不会触发 GC 行为。
vmtool
里提供了强制 GC 的功能:
bash复制代码vmtool --action forceGc
复制代码
如果应用配置了-verbose:gc
参数,则可以在应用的标准输出里看到类似的日志:
csharp复制代码[GC (JvmtiEnv ForceGarbageCollection) 25760K->17039K(349696K), 0.0015299 secs]
[Full GC (JvmtiEnv ForceGarbageCollection) 17039K->16840K(353792K), 0.0154049 secs]
复制代码
致谢
总结
上面只展示使用vmtool
命令操作 spring context 的例子。实际上可以应用在很多方面,比如:
欢迎大家到 Issue 里分享使用经验: github.com/alibaba/art…
评论