如何在OpenJ9场景下使用Arthas

发布时间 2023-09-06 12:19:00作者: sackvim
Alibaba开源的Arthas是一个非常有名的Java诊断工具,他可以解析JVM的运行资源占用,运行状况,可以查看类的加载过程,使用的类加载器等等。但是比较可惜的是,他没有对于OpenJ9做出额外的支持,因此当你的JVM选择OpenJ9后,使用arthas可能会存在一定问题。本文将从我的亲身使用出发,看看OpenJ9在使用Arthas时会遇到哪些问题?
  dashboard
  dashboard是arthas的指令之一,该指令用于展示当前系统的实时面板。但是在实际使用中会发现,如果OpenJ9中启用-Xgcpolicy:balanced的gc策略,会报如下的错误:
  [arthas@73192]$ dashboard
  process dashboard failed: init argument cannot be less than -1

  查看Arthas的日志文件,文件中有更加详细的日志:

  java.lang.IllegalArgumentException: init argument cannot be less than -1
  at java.lang.management.MemoryUsage.<init>(MemoryUsage.java:94)
  at com.ibm.java.lang.management.internal.MemoryPoolMXBeanImpl.getUsageImpl(Native Method)
  at com.ibm.java.lang.management.internal.MemoryPoolMXBeanImpl.getUsage(MemoryPoolMXBeanImpl.java:235)
  at com.taobao.arthas.core.command.monitor200.MemoryCommand.getUsage(MemoryCommand.java:82)
  at com.taobao.arthas.core.command.monitor200.MemoryCommand.memoryInfo(MemoryCommand.java:52)
  at com.taobao.arthas.core.command.monitor200.DashboardCommand$DashboardTimerTask.run(DashboardCommand.java:245)
  at java.util.TimerThread.mainLoop(Timer.java:555)
  at java.util.TimerThread.run(Timer.java:505)

  本来以为是Arthas对于OpenJ9不兼容导致的,但是经过一番研究发现事情并不是那么简单。

  写一个最简单的程序来进行验证:
public static void main(String[] args) {
    List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();

    for (MemoryPoolMXBean memoryPoolMXBean : memoryPoolMXBeans) {
        memoryPoolMXBean.getUsage();
    }
}

  这么简单一个demo居然在OpenJ9 balanced gc策略下抛出了异常:

  Exception in thread "main" java.lang.IllegalArgumentException: init argument cannot be less than -1
  at java.management/java.lang.management.MemoryUsage.<init>(MemoryUsage.java:94)
  at java.management/com.ibm.java.lang.management.internal.MemoryPoolMXBeanImpl.getUsageImpl(Native Method)
  at java.management/com.ibm.java.lang.management.internal.MemoryPoolMXBeanImpl.getUsage(MemoryPoolMXBeanImpl.java:235)
  at org.example.openj9.Test.main(Test.java:12)

  经过测试验证,确认了这个是OpenJ9的bug,错怪Arthas了!

  经过和OpenJ9社区的沟通,发现这个bug会在空闲的region数小于Eden region数时产生,此时的"reserved size"会小于0,并导致报错。遗憾的是,此bug目前还未修复,预计将在后续的7月份的版本中进行修复并发布。
  trace
  使用trace可以排查具体的类的加载时长,排查执行慢的原因。不过在OpenJ9场景下使用trace会出现如下报错:
[arthas@22508]$ trace org.springframework* * '#cost > 1000'
Affect(class count: 4180 , method count: 33388) cost in 28438 ms, listenerId: 1
Enhance error! exception: java.lang.VerifyError
error happens when enhancing class: null, check arthas log: /Users/logs/arthas/arthas.lo
java.lang.VerifyError: null
        at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
        at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:156)
        at com.taobao.arthas.core.advisor.Enhancer.enhance(Enhancer.java:446)
        at com.taobao.arthas.core.command.monitor200.EnhancerCommand.enhance(EnhancerCommand.java:162)
        at com.taobao.arthas.core.command.monitor200.EnhancerCommand.process(EnhancerCommand.java:109)
        at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
        at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
        at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
        at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
        at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:826)

       查看日志发现如上报错:

  这让我百思不得其解,没办法只好求助Arthas社区,可是得到的结果就是Arthas不支持OpenJ9。没办法,既然不支持,那么我们就换种办法来排查问题。

  火焰图
  Arthas火焰图是基于开源项目async-profiler实现的,async-profiler使用C++实现。支持生成应用热点的火焰图。其也可以用于排查应用执行慢的问题。他本质上是通过不断的采样,然后把收集到的采样结果生成火焰图。
  然而在实际的使用中,会发现火焰图也不支持OpenJ9。
  没办法,又又又只能去社区寻找答案。最终经过排查,发现async-profiler是做了OpenJ9的支持的,不过,这些支持在2.7+版本后才陆续合并入主分支,而Arthas的最新版本引用的是2.6.x的async-profiler,因此不支持OpenJ9也是情理之中了。
  于是我们只好自己动手丰衣足食。好在Arthas集成async-profiler的方法非常粗暴,是直接使用的so文件,于是我们直接替换Arthas的async-profiler目录下的几个so文件,将之替换为最新版本的,果然火焰图能力就能够正常使用了。
  后续在提了Issue之后,Arthas社区近期反应已经将async-profiler升级至了高版本,因此只要下载最新版本就能让OpenJ9也可以使用Arthas的火焰图能力了。
  总结
  OpenJ9虽然有其优势,但是在实际中的用户远远不如HotSpot,因此在各个开源项目中的支持还远远不够。在折腾Arthas的过程中遇到了无数的坑,而且很多还是无法简单解决的,本文只是简单选取几个遇到的典型问题,希望能够有相同需求的朋友们能够一起来 踩坑 探索。