天天看點

開源工具高效分析Java應用 開源工具高效分析Java應用

 不止一次,我們都萌發過想對運作中程式的底層狀況一探究竟的念頭。産生這種需求的原因可能是運作緩慢的服務、Java虛拟機(JVM)崩潰、挂起、死鎖、頻繁的JVM暫停、突然或持續的高CPU使用率、甚至于可怕的記憶體溢出(OOME)。好消息是現在已有許多工具能幫你得到Java虛拟機運作過程中的不同參數,這些資訊有助于你了解其内部狀況,進而診斷上述的各種情況。

  好了,讓我們出發。

  如果程式出現不正常的高記憶體負載、頻繁無響應或記憶體溢出,通常最好的分析切入點是檢視記憶體對象。幸好JVM内置了工具“jmap”,讓它天生就能完成這種任務。

  Jmap(借助JPM的一點幫助)

  為了運作jmap,你需要知道被調試程式的PID(程序辨別符)。得到PID的簡單辦法是使用JVM提供的jps,它能列出機器上每一個JVM程序及其PID。jps輸出結果如下圖:

開源工具高效分析Java應用 開源工具高效分析Java應用

圖1:jps指令的終端輸出

  為了列印記憶體統計圖,我們需要打開jmap控制台程式,并輸入程式的PID和“-histo:live”選項。如果不添加這個選項,jmap将完整導出該程式的堆記憶體,這不是我們想要的結果。是以,如果想得到上圖中“eureka.Proxy”程式的記憶體統計圖,我們應該用如下指令來運作jmap:

  jmap –histo:live 45417

  上述指令輸出如下:

開源工具高效分析Java應用 開源工具高效分析Java應用

圖2:指令jmap -histo:live的輸出結果顯示了堆中現有對象的個數

  結果中每行顯示了目前堆中每種類類型的資訊,包含被配置設定的執行個體個數及其消耗的位元組數。

  本例中,我請同僚有意給程式增加了一處明顯的記憶體洩露。請特别注意位于第8行的類,CelleData。将它與下圖顯示的4分鐘後截屏進行比較:

開源工具高效分析Java應用 開源工具高效分析Java應用

圖3:jmap的輸出表明CelleData類的對象數目增加了

  請注意CelleData類現在已經變為系統中第二多的類,短短4分鐘内已經增加了631,701個額外執行個體。等待約一小時後,我們觀察到如下結果:

開源工具高效分析Java應用 開源工具高效分析Java應用

圖4:程式執行1小時後jmap的輸出結果,顯示超過2千5百萬個CelleData類執行個體

  現在有超過2千5百萬個CelleData類執行個體,占用了超過1GB記憶體!我們可以确認這是一個記憶體洩露。

  這類資料資訊的好處是,不僅非常有用而且對于很大的JVM堆也能快速回報結果。我曾經試過檢測一個運作頻繁并且占用17GB堆記憶體的程式,使用jmap能夠在1分鐘内生成程式的性能統計圖。

  需要注意的是,jmap不是運作分析工具,在生成統計圖時JVM可能會暫停,是以當生成統計圖時需要确認這種暫停對程式是可接受的。以我的經驗,通常在調試一個嚴重bug時需要生成這種統計圖,這種情況下,這些1分鐘的暫停對程式來說是可接受的。這裡,我們引出了下一個話題 - 半自動的運作分析工具VisualVM。

  VisualVM

  另一個包含于JVM中的工具是VisualVM,它的開發者将它描述為“一種內建了多個JDK指令行工具的可視化工具,它能為您提供輕量級的運作分析能力”。這樣看來,VisualVM是另一種你最有可能用到的事後分析工具,一般是錯誤已出現或性能問題已經用傳統方法(客戶抱怨大多屬于此類)發現。

  繼續之前的示例程式和它嚴重的記憶體洩露問題,在程式執行30分鐘後,VisualVM幫我們繪制了如下圖表:

開源工具高效分析Java應用 開源工具高效分析Java應用

圖5:程式初始運作的VisualVM 記憶體圖

  從這個圖表,我們可以清晰地看到截止到7:00pm,運作僅僅10分鐘後,程式已經消耗掉超過1GB的堆空間。又過了23分鐘,JVM已經到了它啟動參數–Xmx3g最大值,導緻程式響應緩慢,系統響應緩慢(持續的垃圾回收)和數量驚人的記憶體溢出錯誤。

  借助jmap,我們定位了這種記憶體消耗攀升的原因。修複後,我們讓程式重新運作于VisualVM的嚴格監測之下,觀察到下面的情況:

開源工具高效分析Java應用 開源工具高效分析Java應用

圖6:修複記憶體洩露問題後的VisualVM記憶體圖

  如你所見,程式的記憶體曲線(啟動參數仍然為–Xmx3g)有了明顯改善。

  除了記憶體圖像工具,VisualVM還提供了一個采樣器和一個輕量級的剖析器(Profiler)。

  VisualVM采樣器能周期采樣程式CPU和記憶體的使用情況。得到的統計資料類似jmap的回報,此外,你還可以通過采樣得到方法調用對CPU的占用情況。它讓你能快速了解周期采樣過程中的方法執行次數:

開源工具高效分析Java應用 開源工具高效分析Java應用

圖7:VisualVM方法執行時間表

  VisualVM剖析器無需對程式周期采樣就可以提供類似采樣器的回報資訊,它還可以收集程式在整個正常執行過程中的統計資料(通過操縱程式源代碼的位元組碼)。從剖析器得到的這種統計資料比從采樣器而來的更精确和實時。

開源工具高效分析Java應用 開源工具高效分析Java應用

圖8:VisualVM剖析器的輸出

  但是,你必須考慮的另一方面是該剖析器屬于一種“暴力”分析工具。它的檢測方法本質上是重新定義程式執行中的大多數類和方法,結果必然會明顯減緩程式執行速度。例如,上述程式運作部分的正常分析,大約要35秒。開啟VisualVM的記憶體剖析器後,導緻程式完成相同分析要31分鐘。

  我們需要清楚的是VisualVM并非功能齊全的剖析器。它無法在你的産品JVM上持續運作,不會儲存分析資料,無法指定門檻值,也不會在超過門檻值時發出警報。要想更多的了解功能齊全的剖析器的目标。下面,讓我們看看BTrace,這個功能齊全的開源java代理程式。

  BTrace

  想象一下,如果能收集JVM目前的任何資訊,那麼你感興趣的資訊有哪些?我猜想問題清單會将因人而異,因情形而異。就個人來說,我通常感興趣的是以下的問題:

  程式對堆、非堆、永久儲存區(Permanent Generation),以及JVM包含的不同記憶體池(新生對象區、長期對象區、存活空間等)的記憶體使用情況

  目前程式的線程數量,以及哪種類型線程正在被使用(單獨計數)

  JVM的CUP負載

  系統平均負載/系統CPU使用總和

  對程式中的某些類和方法,我需要了解它們被調用次數,各自平均執行時間和整體平均時間

  對SQL調用的調用計數及執行次數

  對硬碟和網絡操作的調用計數及執行次數

  利用BTrace可以采集到所有以上資訊,你可以使用BTrace腳本定義需要采集的資料。友善的是,BTrace腳本就是普通Java類,包含一些特殊注解來定義BTrace在什麼地方及如何跟蹤你的程式。BTrace腳本會被BTrace編譯器-btracec編譯成标準的.class檔案。

  BTrace腳本包含許多部分,正如下圖所示。如果需要了解下圖腳本的詳細内容,請點選該連結或通路BTrace項目網站。

  由于BTrace僅僅是一個代理,記錄結果後,它的任務就算完成了。除了文本輸出,BTrace并不具備動态展現被收集資訊的功能。預設情況下,BTrace腳本輸出結果将在btrace.class檔案所在位置生成一個名為BTrace腳本名.class.btrace的text檔案。

  我們可以通過給BTrace設定一個額外參數,讓它按某時間間隔循環記錄日志。切記,它最多能在100個日志檔案間循環,當達到*.class.btrace.99,它将覆寫*.class.btrace.00檔案。若讓循環間隔在一個合理數字(如,每7.5秒)内,你就有充足時間來處理這些輸出。隻要在java代理的輸入參數中加上fileRollMilliseconds=7500,就可以實作日志循環。

  BTrace一大缺點是它比較原始,難以定義它的輸出格式。你也許非常希望有一種更好的方式來處理BTrace的輸出和資料,比如可以用一種一緻的圖形使用者界面來展示。你可能還需要比較不同時間點的資料和超出門檻值能發送警告。一個新的開源工具EurekaJ,就此應運而生。

開源工具高效分析Java應用 開源工具高效分析Java應用

圖9:激活方法分析時必需的BTrace腳本

開源工具高效分析Java應用 開源工具高效分析Java應用

本文出自seven的測試人生公衆号最新内容請見作者的GitHub頁:http://qaseven.github.io/