Arthas实践经验总结

arthas 基本使用

https://arthas.aliyun.com/doc/

查看java内存中对象

vmtool --action getInstances --className com.zaxxer.hikari.pool.HikariPool -x 3

用来查看连接池信息,可以在没有工具类的情况下,查看spring容器中的信息

arthas之火焰图

火焰图使用
火焰图之官方文档

查询mybatis的sql执行语句

查询Mybatis执行的sql:
经常不知道mysql执行的什么语句,可以用如下语句去监控mysql的执行语句。
注意UPDATE 即代表 更新。 SELECT 同理。

watch  org.apache.ibatis.mapping.BoundSql getSql -n 5 'returnObj' 'returnObj.indexOf("UPDATE")!=-1'
1

这里returnObj是观察了返回值

动态jvm参数

使用dashboard命令查看GC信息

在dashboard命令里可以直接看到GC的数据,次数和时间:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wbegTK2A-1658635420146)(https://www.wangdaye.net/upload/2021/11/image-e2b1a567ab33471c89affaa5cf89f2d4.png)]

使用vmoption命令动态打开GC日志
$ vmoption PrintGC true
Successfully updated the vm option.
 NAME     BEFORE-VALUE  AFTER-VALUE
------------------------------------
 PrintGC  false         true
$ vmoption PrintGCDetails true
Successfully updated the vm option.
 NAME            BEFORE-VALUE  AFTER-VALUE
-------------------------------------------
 PrintGCDetails  false         true

打开上面两个选项之后,当应用发生GC时,就会在标准输出里打印GC日志。

使用vmtool强制GC
$ vmtool --action forceGc

[GC (JvmtiEnv ForceGarbageCollection) [PSYoungGen: 2184K->352K(76288K)] 19298K->17474K(166912K), 0.0011562 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[Full GC (JvmtiEnv ForceGarbageCollection) [PSYoungGen: 352K->0K(76288K)] [ParOldGen: 17122K->16100K(90112K)] 17474K->16100K(166400K), [Metaspace: 20688K->20688K(1069056K)], 0.0232947 secs] [Times: user=0.14 sys=0.01, real=0.03 secs]
在GC前后,执行heapdump

在排查GC问题时,我们有时需要对比GC前后,生成heapdump文件,然后再做对比。

  • 打开HeapDumpBeforeFullGC开关,可以在GC前生成heapdump文件
  • 打开HeapDumpAfterFullGC开关,可以在GC结束后生成heapdump文件
$ vmoption HeapDumpBeforeFullGC true
Successfully updated the vm option.
 NAME                  BEFORE-VALUE  AFTER-VALUE
-------------------------------------------------
 HeapDumpBeforeFullGC  false         true
$ vmtool --action forceGc

再使用vmtool --action forceGc强制GC,则可以在GC日志中发现heapdump信息,并且在应用目录下会生成heapdump hprof文件:

#9: [Heap Dump (before full gc): Dumping heap to java_pid69445.hprof ...
Heap dump file created [23915304 bytes in 0.107 secs]

再使用其它堆分析软件,对比两个heapdump文件,就可以知道GC到底回收了哪些对象。

在GC前后,打印类直方图

排查GC问题时,我们有时需要统计每个类加载的数量和占用内存大小。

  • 打开`PrintClassHistogramBeforeFullGC`开关,可以在GC前打印类直方图
  • 打开`PrintClassHistogramAfterFullGC`开关,可以在GC结束后打印类直方图
$ vmoption PrintClassHistogramBeforeFullGC true
Successfully updated the vm option.
 NAME                             BEFORE-VALUE  AFTER-VALUE
------------------------------------------------------------
 PrintClassHistogramBeforeFullGC  false         true
$ vmtool --action forceGc

再使用vmtool --action forceGc强制GC,在GC日志中会打印类直方图,可以直观知道每个类的instances数量,占用内存大小:

#13: [Class Histogram (before full gc):
 num     #instances         #bytes  class name
----------------------------------------------
   1:         24519        5783400  [C
   2:          5648        5102712  [B
   3:          3685         888128  [Ljava.lang.Object;
   4:          3255         619560  [I
   5:         24263         582312  java.lang.String
   6:          4227         475320  java.lang.Class
   7:          1288         402112  [Ljava.util.HashMap$Node;
   8:            75         296160  [Ljava.nio.channels.SelectionKey;
   9:          6759         216288  java.util.HashMap$Node
  10:          2069         182072  java.lang.reflect.Method
  11:          3326         133040  java.util.LinkedHashMap$Entry

Thread

介绍
   jstack 
   很相似,但是功能更加强大,主要是查看当前 
   JVM 
   的线程堆栈信息 

同时可以结合使用 
   thread –b 
   来进行死锁的排查死锁。 

参数解释: 

-n 
   指定最忙的前 
   n 
   个线程并打印堆栈
   -b 
   找出阻塞当前线程的线程 

-i 
   指定 
   cpu 
   占比统计的采样间隔,单位为毫秒 

实战演示

thread 显示线程信息

thread –h
显示帮助

thread –b 找出阻塞当前线程的线程

如图:

如果有死锁,会有红色的字提醒着,这个阻塞的线程已经被另外一个线程阻塞。

采样

thread -i 1000 -n 3

每过
1000
毫秒进行采样,显示最占
CPU
时间的前
3
个线程

thread --state WAITING

查看处于等待状态的线程

3、JVM

其他小技巧

查看第一个参数:
$ watch com.taobao.container.Test test "params[0]"
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 34 ms.
@ArrayList[
    @Pojo[com.taobao.container.Test$Pojo@75a1cd57],

    @Pojo[com.taobao.container.Test$Pojo@3d012ddd],

    @Pojo[com.taobao.container.Test$Pojo@6f2b958e],

    @Pojo[com.taobao.container.Test$Pojo@1eb44e46],

    @Pojo[com.taobao.container.Test$Pojo@6504e3b2],
查看第一个参数的size:
$ watch com.taobao.container.Test test "params[0].size()"
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 22 ms.
@Integer[40]
将结果按name属性投影:
$ watch com.taobao.container.Test test "params[0].{ #this.name }"
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 25 ms.
@ArrayList[
    @String[name 0],

    @String[name 1],
按条件过滤:
$ watch com.taobao.container.Test test "params[0].{? #this.name == null }" -x 2
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 27 ms.
@ArrayList[
    @Pojo[
        name=null,
        age=@Integer[32],
        hobby=null,
    ],
]
@ArrayList[
    @Pojo[
        name=null,
        age=@Integer[31],
        hobby=null,
    ],
]

$ watch com.taobao.container.Test test "params[0].{? #this.name != null }" -x 2
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 24 ms.
@ArrayList[
    @Pojo[
        name=@String[name 1],
        age=@Integer[3],
        hobby=null,
    ],
过滤后统计:
$ watch com.taobao.container.Test test "params[0].{? #this.age > 10 }.size()" -x 2
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 29 ms.
@Integer[31]
@Integer[31]
访问静态变量
  • 在watch命令中访问如下,但是会受到classloader的限制,不推荐使用
$ watch com.taobao.container.Test test "@com.taobao.container.Test@m"
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 36 ms.
@HashMap[
    @String[a]:@String[aaa],

    @String[b]:@String[bbb],
]
  • 使用新版本中的getstatic命令,通过-c指定classloader,可以查看任意static变量,同时支持ognl表达式处理
$ getstatic com.alibaba.arthas.Test n 'entrySet().iterator.{? #this.key.name()=="STOP"}'
field: n
@ArrayList[
    @Node[STOP=bbb],
]
Affect(row-cnt:1) cost in 68 ms.

$ getstatic com.alibaba.arthas.Test m 'entrySet().iterator.{? #this.key=="a"}'
field: m
@ArrayList[
    @Node[a=aaa],
]
调用静态方法(需手动触发test方法以触发执行ognl表达式)

方法一

$ watch com.taobao.container.Test test "@java.lang.Thread@currentThread()"

方法二

ognl '@java.lang.System@out.println("hello ognl")'
调用静态方法再调用非静态方法(同上)
$ watch com.taobao.container.Test test "@java.lang.Thread@currentThread().getContextClassLoader()"
watch/monitor/trace 等判断重载函数/同名函数

第一种方式,判断params的length:

$ watch Test hello params params.length==2
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:2) cost in 21 ms.
ts=2019-01-07 19:50:17; [cost=0.103617ms] result=@Object[][
    @Integer[109],
    @String[world],
]
ts=2019-01-07 19:50:18; [cost=0.185045ms] result=@Object[][
    @Integer[110],
    @String[world],
]

第二种方式,判断params的类型(注意,这里因为int会被包装为Object,所以params[0]的类型是java.lang.Integer):

$ watch Test hello params 'params[0].class.name=="java.lang.Integer"'
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:2) cost in 25 ms.
ts=2019-01-07 19:54:59; [cost=0.358485ms] result=@Object[][
    @Integer[390],
    @String[world],
]
ts=2019-01-07 19:55:00; [cost=0.211562ms] result=@Object[][
    @Integer[391],
    @String[world],
]
获取spring之context

Arthas之调用SpringBoot获取Bean · wangdaye’s blog

自定义static获取Bean之工具类
arthas之热更新操作

Arthas之热更新骚操作 · wangdaye’s blog

定位到内部类
sc com.ydl.message.service.facade.JPushFacadeImpl$AspectCall
如何trace到更深层之trace/watch之连续自动增强

以前在trace或者watch多个类和函数时,需要写正则表达式匹配多个对象。使用非常不方便。

尝试一个新特性,给已运行的 trace/watch 命令增加新的匹配类和函数。

原理是,每个command都和一个 listener 关联;新执行一个command,传入 listener id,然后查找到已有的 listener,然后增强。

测试版本可以在这里下载: #1184

在窗口1里对 arthas-demo 进行trace trace demo.MathGame run

[arthas@71839]$ trace demo.MathGame run
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 87 ms, listenerId: 1
`---ts=2020-05-25 01:52:27;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
    `---[0.849849ms] demo.MathGame:run()
        +---[0.142048ms] demo.MathGame:primeFactors() #24
        `---[0.177514ms] demo.MathGame:print() #25

可以看到 listenerId是1 。

然后打开一个新的窗口, telnet localhost 3658 ,执行 trace demo.MathGame primeFactors --listenerId 1

[arthas@71839]$ trace demo.MathGame primeFactors --listenerId 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 37 ms, listenerId: 1

可以看到窗口1里增加了trace深度:

`---ts=2020-05-25 01:54:01;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
    `---[0.254854ms] demo.MathGame:run()
        `---[0.125945ms] demo.MathGame:primeFactors() #24 [throws Exception]
            `---[0.07948ms] demo.MathGame:primeFactors()
                `---[0.024928ms] throw:java.lang.IllegalArgumentException() #46

`---ts=2020-05-25 01:54:02;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
    `---[0.287046ms] demo.MathGame:run()
        +---[0.108534ms] demo.MathGame:primeFactors() #24
        |   `---[0.066629ms] demo.MathGame:primeFactors()
        `---[0.072959ms] demo.MathGame:print() #25
Arthas之使用火焰图




  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值