1. 程式人生 > >JVM 效能調優實戰之 使用阿里開源工具 TProfiler 在海量業務程式碼中精確定位效能程式碼

JVM 效能調優實戰之 使用阿里開源工具 TProfiler 在海量業務程式碼中精確定位效能程式碼

               

本文是《JVM 效能調優實戰之:一次系統性能瓶頸的尋找過程》 的後續篇,該篇介紹瞭如何使用 JDK 自身提供的工具進行 JVM 調優將 TPS 由 2.5 提升到 20 (提升了 7 倍),並準確定位系統瓶頸:我們應用裡靜態物件不是太多、有大量的業務執行緒在頻繁建立一些生命週期很長的臨時物件,程式碼裡有問題。那麼問題來了,如何在海量業務程式碼裡邊準確定位這些效能程式碼?本文將介紹如何使用阿里開源工具 TProfiler 來定位這些效能程式碼,成功解決掉了 GC 過於頻繁的效能瓶頸,並最終在上次優化的基礎上將 TPS 再提升了4 倍,即提升到 100。

1. TProfiler 的下載安裝

1.1. 下載

訪問 TProfiler 的 GitHub 主頁,點選 Clone or download 按鈕的開啟下載選項,點選該選項下的 Download ZIP 按鈕將 TProfiler-master.zip 下載到本地。筆者上傳了一份截至 20160920 最新 TProfiler-master.zip 到 CSDN 資源,讀者朋友也可以去這裡下載:http://download.csdn.net/detail/defonds/9635731

1.2. 安裝

SSH 登入需要監控的遠端伺服器主機,為 TProfiler 新建安裝路徑:$ mkdir /opt/tprofiler本地將下載後的 TProfiler-master.zip 解壓縮,將 dist 目錄下的 profile.properties 以及 dist/lib 目錄下的 tprofiler-1.0.1.jar ftp 上傳到遠端伺服器 /opt/tprofiler 目錄下。最後將遠端伺服器 /opt/tprofiler 目錄及其下所有檔案的所有者改為啟動 Weblogic 程序的使用者及其所在使用者組。

2. TProfiler 的配置部署

2.1. TProfiler 配置

編輯伺服器 /opt/tprofiler/profile.properties 檔案內容如下:#log file namelogFileName = tprofiler.logmethodFileName = tmethod.logsamplerFileName = tsampler.log#basic configuration itemsstartProfTime = 9:00:00endProfTime = 23:00:00eachProfUseTime = 5eachProfIntervalTime = 50samplerIntervalTime = 20port = 30000

debugMode = falseneedNanoTime = falseignoreGetSetMethod = true#file pathslogFilePath = ${user.home}/logs/${logFileName}methodFilePath = ${user.home}/logs/${methodFileName}samplerFilePath = ${user.home}/logs/${samplerFileName}#include & excludes itemsexcludeClassLoader = org.eclipse.osgi.internal.baseadaptor.DefaultClassLoaderincludePackageStartsWith = com.caucho;com.defonds;com.fasterxml;com.sun.jersey;com.sun.jmx;org.apache;org.codehaus;org.jdbcdslog;org.mybatis;org.quartz;org.springframeworkexcludePackageStartsWith = com.taobao.sketch;org.apache.velocity;com.alibaba;com.taobao.forest.domain.dataobject紅色部分是我們修改後的內容,其它部分使用預設值。

2.2. Weblogic 啟動引數配置

在 Weblogic JVM 啟動引數里加入:-javaagent:/opt/tprofiler/tprofiler-1.0.1.jar -Dprofile.properties=/opt/tprofiler/profile.properties之後重啟 Weblogic。

3. TProfiler 的遠端操作

使用啟動 Weblogic 程序的使用者 SSH 遠端登入正在進行壓測的機器。

3.1. 檢視 TProfiler 當前狀態

$ java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 statusrunning得到這個結果證明 TProfiler 正在進行採集工作。

3.2. 將 TProfiler 停止,以釋放其佔用的系統資源

隨時關閉 TProfiler:$ java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 stop$ java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 statusstop隨時啟動以繼續採集:$ java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 start$ java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 statusrunning

3.3. 刷出資料

$ java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.client.TProfilerClient 127.0.0.1 30000 flushmethod會將資料刷出到 ~/logs/ 目錄下:TProfiler的日誌.png

4. TProfiler 對效能方法的採集

4.1. 普通方法、執行緒統計

$ java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.analysis.SamplerLogAnalysis ~/logs/tsampler.log ~/logs/method.log ~/logs/thread.log

4.2. top 統計

$ java -cp /opt/tprofiler/tprofiler-1.0.1.jar com.taobao.profile.analysis.ProfilerLogAnalysis ~/logs/tprofiler.log ~/logs/tmethod.log ~/logs/topmethod.log ~/logs/topobject.log方法執行時間統計:這個非常非常重要,這個是 TProfiler 最最重要的 feature,是其能夠傲視所有其他效能測試類(包括 jvm 效能測試類)軟體的關鍵所在,我們將會不止一次地在關鍵的時候受益於 TProfiler 的這一非常有用的特性。上述命令刷出的 topmethod.log 部分結果如下:com/defonds/core/ppts/common/support/JsonUtils:object2jsonString:123 13519 154 2083584com/caucho/hessian/client/HessianURLConnection:sendRequest:156 15894 130 2072565com/defonds/rest/core/client/proxy/ResourceJsonInvocationHandler:invoke:39 8123 113 921340com/defonds/core/ppts/cache/service/impl/MerBankCfgServiceImpl:selectMerBankCfgByParams:72 54213 15 799322com/defonds/core/ppts/incomes/biz/sinopay/service/impl/SinoPayBankReturnServiceImpl4Json:updateOrderSuccess:792 2495 176 438542com/defonds/core/ppts/common/support/framework/bean/Message:<init>:76 6219 26 163741com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer:serializeContents:107 51883 3 145556com/defonds/core/ppts/cache/biz/cims/impl/AccountPrdAndBankCacheImpl:selectBasicProductCfg:144 16131 8 137029格式說明:方法資訊 執行次數 平均執行時間(單位:ms) 全部執行時間(單位:ms)

5. 效能方法的優化

根據 topmethod.log 統計結果,我們拿到了熱點方法 top10:

                                                                                                                          
熱點方法 top10
方法名被呼叫次數平均執行時間(ms)取樣內總執行時間(ms)
com/defonds/core/ppts/common/support/JsonUtils:object2jsonString:123135191542083584
com/caucho/hessian/client/HessianURLConnection:sendRequest:156158941302072565
com/defonds/rest/core/client/proxy/ResourceJsonInvocationHandler:invoke:398123113921340
com/defonds/core/ppts/cache/service/impl/MerBankCfgServiceImpl:selectMerBankCfgByParams:725421315799322
com/defonds/core/ppts/incomes/biz/sinopay/service/impl/SinoPayBankReturnServiceImpl4Json:updateOrderSuccess:7922495176438542
com/defonds/core/ppts/common/support/framework/bean/Message:<init>:76621926163741
com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer:serializeContents:107518833145556
com/defonds/core/ppts/cache/biz/cims/impl/AccountPrdAndBankCacheImpl:selectBasicProductCfg:144161318137029
com/defonds/core/ppts/common/jms/retrieve/listener/DefaultMessageListener:handleMessage:64298146136180
com/fasterxml/jackson/databind/ser/BeanPropertyWriter:serializeAsField:573538922112553

這是壓測時根據多次取樣結果,揀選出的一次比較有代表性的一次。紅色部分值得我們去重點關注並優化一下,因為極有可能就是應用瓶頸所在。這些程式碼要麼是導致平均響應時間低下的一些點,要麼是導致大量臨時物件產生的一些點。對於上篇部落格中的結論,這些程式碼的調優原則是:臨時物件能改成靜態物件進行復用就改成公用物件否則要想方設法縮短其生命週期;高頻訪問程式碼提高響應速度。根據 jvm gc 日誌發現很多 young gc 之後堆記憶體已用空間不僅下降反而上升至最大使用量導致 full gc,臨時物件如果可以和其它執行緒複用的話改成靜態物件以減少大量執行緒 local 物件的產生。以排名第一的熱點方法 com/defonds/core/ppts/common/support/JsonUtils:object2jsonString:123 為例,看看如何來進行調優。

import org.codehaus.jackson.map.ObjectMapper; public static <T> String object2jsonString(T t) {  try {   ObjectMapper objectMapper = instanceObjectMapper();   return objectMapper.writeValueAsString(t);  } catch (JsonParseException e) {   log.error(e.getMessage(), e);   throw new SysException(e);  } catch (JsonMappingException e) {   log.error(e.getMessage(), e);   throw new SysException(e);  } catch (IOException e) {   log.error(e.getMessage(), e);   throw new SysException(e);  } } public static ObjectMapper instanceObjectMapper() {  JsonFactory jf = new JsonFactory();  jf.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true);  return new ObjectMapper(jf); } 

該熱點方法的優化建議:這個方法平均呼叫時間在 154ms,如果在低併發時可能比這要小得多。但是高併發時可能要等待 GC 的堆記憶體釋放、GC 作業時對業務執行緒造成的暫停時間等因素影響,這個時間會被無限放大。

5.1. 臨時物件改成靜態物件

object2jsonString 方法的 objectMapper 物件,instanceObjectMapper 方法的 jf 物件;

5.2. json 處理由 jackson 改為 fastjson

jackson 和 spring 整合的很好,提供的功能點很多很強大。但是其效能未必靠得住。比如我們原來用過谷歌的 Gson 進行 json 處理,某個大物件的 json 解析使用 gson 是 100 多秒,而換成 fastjson 解析後是 900 多毫秒。上百倍的效能差距呀,這還是在單使用者操作、不能存在 CPU 和記憶體等資源限制及競爭的情況下拿到的資料。在此向貢獻出 fastjson 的阿里人致敬~

5.3. 頻繁 GC 的瓶頸已不復存在

針對 TProfiler 幫我們在海量業務程式碼中定位到的 top5 效能程式碼進行優化後,部署重新壓測,50 個使用者併發兩個小時左右,我們拉了幾次快照,上篇部落格中定位的頻繁 GC 的效能瓶頸已不復存在,TRT 也由上篇部落格優化到的 2.5 下降到 0.5,TPS 基本能穩定在 100 個。問題圓滿解決。

6. 需要注意的一些問題

6.1. TProfiler 埠號是否已被佔用

為 TProfiler 選取埠號之前要先檢測一下該埠號是否已被佔用:$ netstat -an | grep 30000

6.2. TProfiler 配置裡 includePackageStartsWith 

一定要根據你自己的系統進行實際更改,不然就會遇到《TProfiler.log的內容為空 #33》的問題,截圖如下:TProfiler.log的內容為空.png

6.3. 取樣時間是否在配置的統計時間之內

在確認 6.2 裡注意的事情無誤的情況下,得到的 tprofiler.log 仍然為空 (只有 = 號) 的話,請檢測你的取樣時間是否在 profile.properties 裡定義的 startProfTime 和 endProfTime 時間段之內。

6.4. 幾個命令配合使用

在壓測的時候,結合使用 start、stop、flushmethod、ProfilerLogAnalysis topmethod 等幾個命令,以拿到關鍵性的結果。如果能再結合 Weblogic、LoadRunner 的啟動、停止,效果最佳。不然的話,如果 JVM 已經跑了很多天,拿到的資料可能不是你想要的,反而會誤導你南轅北轍。

7. 後記

總體來講,TProfiler 配置部署、遠端操作、日誌閱讀都不太複雜,操作還是很簡單的。但是其卻是能夠起到一針見血、立竿見影的效果,幫我們解決了 GC 過於頻繁的效能瓶頸。TProfiler 最重要的特性就是能夠統計出你指定時間段內 JVM 的 topmethod,這些 topmethod 極有可能就是造成你 JVM 效能瓶頸的元凶。這是其他大多數 JVM 調優工具所不具備的,包括 JRockit Mission Control。JRokit 首席開發者 Marcus Hirt 在其私人部落格《Low Overhead Method Profiling with Java Mission Control》下的評論中曾明確指出 JRMC 並不支援 TOP 方法的統計:JRMC並不支援TOP方法的統計.png最後再次向具備開源精神的阿里技術團隊致敬~

參考資料