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.log
檢視日誌發現如下報錯:
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的過程中遇到了無數的坑,而且很多還是無法簡單解決的,本文只是簡單選取幾個遇到的典型問題,希望能夠有相同需求的朋友們能夠一起來 踩坑 探索。