troubleshoot之:使用JFR分析性能問題

簡介

java程式的性能問題分析是一個很困難的問題。尤其是對於一個非常複雜的程式來說,分析起來更是頭疼。

還好JVM引入了JFR,可以通過JFR來監控和分析JVM的各種事件。通過這些事件的分析,我們可以找出潛在的問題。

今天我們就來介紹一下對java性能分析比較重要的一些JFR事件。

GC性能事件

一般來說,GC會對java程式的性能操作產生比較重要的影響。我們可以使用jfr監控jdk.GCPhasePause事件。

下面是一個jdk.GCPhasePause的例子:

jfr print --events jdk.GCPhasePause flight_recording_1401comflydeanTestMemoryLeak89268.jfr

輸出結果:

jdk.GCPhasePause {
  startTime = 19:51:49.798
  duration = 41.1 ms
  gcId = 2
  name = "GC Pause"
}

通過GCPhasePause事件,我們可以統計總的GC pause時間和平均每一次GC pause的時間。

一般來說GC是在後台執行的,所以GC本身的執行時間我們並不需要關注,因為這並不會影響到程式的性能。我們需要關注的是應用程式因為GC暫停的時間。

考慮下面兩種情況,第一種單獨的GC導致GC pause時間過長。第二種是總的GC pause時間過長。

如果是第一種情況,那麼可能需要考慮換一個GC類型,因為不同的GC類型在pause時間和吞吐量的平衡直接會有不同的處理。同時我們需要減少finalizers的使用。

如果是第二種情況,我們可以從下面幾個方面來解決。

  • 增加heap空間大小。heap空間越大,GC的間隔時間就越長。總的GC pause時間就會越短。

  • 盡量減少tmp對象的分配。我們知道為了提升多執行緒的性能,JVM會使用TLAB技術。一般來說小對象會分配在TLAB中,但如果是大對象,則會直接分配在heap空間中。但是大部分對象都是在TLAB中分配的。所以我們可以同時關注TLAB和TLAB之外的兩個事件:jdk.ObjectAllocationInNewTLAB和dk.ObjectAllocationOutsideTLAB。

  • 減少分配頻率。我們可以通過jdk.ThreadAllocationStatistics來分析。

同步性能

在多執行緒環境中,因為多執行緒會競爭共享資源,所以對資源的同步,或者鎖的使用都會影響程式的性能。

我們可以監控jdk.JavaMonitorWait事件。

jfr print --events jdk.JavaMonitorWait flight_recording_1401comflydeanTestMemoryLeak89268.jfr

我們看一個結果:

jdk.JavaMonitorWait {
  startTime = 19:51:25.395
  duration = 2 m 0 s
  monitorClass = java.util.TaskQueue (classLoader = bootstrap)
  notifier = N/A
  timeout = 2 m 0 s
  timedOut = true
  address = 0x7FFBB7007F08
  eventThread = "JFR Recording Scheduler" (javaThreadId = 17)
  stackTrace = [
    java.lang.Object.wait(long)
    java.util.TimerThread.mainLoop() line: 553
    java.util.TimerThread.run() line: 506
  ]
}

通過分析JavaMonitorWait事件,我們可以找到競爭最激烈的鎖,從而進行更深層次的分析。

IO性能

如果應用程式有很多IO操作,那麼IO操作也是會影響性能的關鍵一環。

我們可以監控兩種IO類型:socket IO和File IO。

相對應的事件有:dk.SocketWrite,jdk.SocketRead,jdk.FileWrite,jdk.FileRead。

程式碼執行的性能

程式碼是通過CPU來運行的,如果CPU使用過高,也可能會影響到程式的性能。

我們可以通過監聽jdk.CPULoad事件來對CPULoad進行分析。

jfr print --events jdk.CPULoad flight_recording_1401comflydeanTestMemoryLeak89268.jfr

看下運行結果:

jdk.CPULoad {
  startTime = 19:53:25.519
  jvmUser = 0.63%
  jvmSystem = 0.37%
  machineTotal = 20.54%
}

如果jvm使用的cpu比較少,但是整個machine的CPU使用率比較高,這說明了有其他的程式在佔用CPU。

如果JVM自己的CPU使用就很高的話,那麼就需要找到這個佔用CPU的執行緒進行進一步分析。

其他有用的event

除了上面提到的event之外,還有一些其他有用的我們可以關注的event。

比如執行緒相關的:jdk.ThreadStart,jdk.ThreadEnd,jdk.ThreadSleep,jdk.ThreadPark。

如果你使用JMC,那麼可以很直觀的查看JFR的各種事件。

所以推薦大家使用JMC。

本文作者:flydean程式那些事

本文鏈接://www.flydean.com/jvm-diagnostic-perform-issue/

本文來源:flydean的部落格

歡迎關注我的公眾號:程式那些事,更多精彩等著您!

Tags: