歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> 更多Linux >> JRockit 提高Linux Java性能的分析

JRockit 提高Linux Java性能的分析

日期:2017/2/27 9:26:33   编辑:更多Linux
  通過分析Java線程堆解決Java應用程序中存在問題的技術。我們可以使用線程堆來分析諸如應用程序掛起,響應時間長以及程序崩潰等情況。在詳細介紹分析線程堆的技術之前,我們先來簡要地看看線程堆本身。     Java線程堆是一個運行中的Java應用程序的所有線程的一個快照。它會顯示一些像當前的堆棧跟蹤、狀態以及線程名稱之類的信息。線程列表中包括由JVM本身創建的線程(負責垃圾收集、信號處理等管理工作)和由應用程序創建的線程。     通過給JVM發送一個SIGQUIT信號,您可以得到一個線程堆。在Unix操作系統(Solaris/Linux/HP-Unix等)中,通過kill-3命令可以得到線程堆。輸出出線程堆之後,應用程序繼續正常運行。當您給JVM發送SIGQUIT信號時,JVM的信號處理器會通過輸出線程堆來響應這一信號。當程序運行的時候,您可以在任何點得到線程堆。     線程堆的一個例子   清單1顯示的就是一個使用Sun JVM 1.4.1的單線程應用程序中的線程堆的例子。main線程是主應用程序線程。所有其他的線程都是由JVM創建的,負責完成一些管理工作。當分析應用程序級的問題時,我們通常只關心應用程序線程。下面,我們來分析清單1中main線程的堆棧跟蹤。     "main" prio=5 tid=0x002358B8 nid=0x7f8 runnable [6f000..6fc40]  at test.method1(test.java:10)  at test.main(test.java:5)    從這個代碼片斷中您可以看到,一個線程堆棧跟蹤有一個名稱、線程優先級(prio=5)、狀態(可運行的)、源代碼行號,以及方法調用。從這個堆棧跟蹤中可以得到如下結論:main線程執行test類的method1方法中的一些代碼。而對method1方法的調用是由同一個類的main方法完成的。您也可以看到那些方法中確切的源代碼行號。     在從一些更復雜的情況去分析線程堆之前,我們先來討論那些可以在線程堆中看到的線程的不同狀態以及它們的意義。     · 可運行的:當獲得CPU的使用權時就可以運行或准備好運行的狀態。JRockit線程堆把這種狀態稱為ACTIVE。     · 在監視中等待:指在一個對象上休眠或等待上述時間,或者等待另一個線程的通知。這種情況會出現在對Thread對象調用任何sleep() 方法或者對Object調用wait() 方法時。     例如,在BEA WebLogic Server中,空閒的執行線程就處於這種狀態,它們會等待一個socket閱讀器線程通知它們去做一些新的工作。堆棧跟蹤如下所示:     "ExecuteThread: '2' for queue: 'weblogic.admin.RMI'"daemon prio=5 tid=0x1752F040 nid=0x180c in Object.wait()[1887f000..1887fd8c]at java.lang.Object.wait(Native Method)waiting on (a weblogic.kernel.ExecuteThread)at java.lang.Object.wait(Object.java:426)    JVM的其他版本把這種狀態稱為CW。JRockit將這種狀態稱為WAITING。     · 等待監視實體:等待鎖住一個對象(其他線程目前正控制著鎖)。如果兩個或者多個線程都想在同一時刻執行一個對象的某些同步代碼塊或方法,就會出現這種情況。請注意,鎖總是加在一個對象上而不是加在獨立的方法上。也就是說,如果一個線程要執行一個對象的同步方法,它必須先給那個對象加鎖。     下面顯示的就是這種情況下的一個線程的堆棧跟蹤的例子:     "ExecuteThread: '24' for queue: 'DisplayExecuteQueue'" daemon prio=5tid=0x5541b0 nid=0x3b waiting for monitor entry [49b7f000..49b7fc24]at weblogic.cluster.replication.ReplicationManager.createSecondary(ReplicationManager.java:908)- waiting to lock (a java.lang.Object)at weblogic.cluster.replication.ReplicationManager.updateSecondary(ReplicationManager.java:715)  在上述代碼片斷中,您們可以看到這個線程已經鎖住了一個對象(6c408700),並且還在等待鎖住另一個對象(6c4b9130)。     JVM的其他版本可能不會在堆棧跟蹤中給出對象ID的加鎖信息。這種情況下,我們可以從線程的狀態中推測出線程正在等待給對象加鎖。同樣的狀態也許會被稱為MW。JRockit將這種狀態稱為LOCKED。     現在,我們來看看各種不同情況下的一些線程堆,並通過分析它們來尋找應用程序中的問題。    死鎖應用程序中的線程堆   在清單2(只顯示了部分線程堆)中,您可以看到JVM已經發現了死鎖並且在線程堆中給出了該信息。消息中很清楚地表明了“ExecuteThread: '47' for queue: 'default'”正在等待給被"ExecuteThread: '57' for queue: 'default'"鎖住的對象加鎖。(這個線程堆來自於WebLogic Server。這些線程都是WebLogic Server的工作線程,它們負責處理客戶機請求。)同時,執行線程47也鎖住了執行線程57正在等待加鎖的一個對象。這就是死鎖。在這種狀態下,這兩個線程都無法繼續運行,它們都在等待另一個線程釋放鎖。     在清單2中,執行線程1正在等待給ConnectionScavanger對象加鎖,但是這個對象目前已經被線程47鎖住了,正如前面提到過的,線程47又被線程57死鎖了。因此,執行線程1也無法繼續運行下去。這最終將導致程序的掛起,因為服務器中的其他執行線程隨後也會試圖獲得那個被執行線程47和57鎖住的對象的鎖。     有些JVM不會給出死鎖信息。它只會給出線程堆。這時候,我們可以通過查看線程的狀態和堆棧跟蹤得出同樣的結論。     上述這個情況中的問題源於WebLogic中的一個bug,後來已經修復了。     其他掛起的情況   在一些情況下,您可能會看到大多數線程都是“可運行”的,但是服務器仍然會掛起,無法響應客戶機的請求。如果某些線程在做IO(輸入輸出)操作,它們可能會在執行read()方法的時候被阻塞住,但是狀態還是“可運行的”(數據庫或其他網絡響應的情況可能很糟)。如果出現這種情況,您需要檢查數據庫和網絡是否正常。     一個“可運行的”線程被阻塞住還可能是由下列原因引起的:     · 程序代碼中的無限循環。這會導致嚴重的CPU占用。     · JVM的垃圾收集可能會運行很長時間,占據大量的CPU時間。     · JVM進程可能會用完文件描述符。     如果應用程序的代碼調用了wait()方法或sleep()方法,線程在休眠時間內不會繼續運行——這時線程的狀態就是“在監視中等待”。出現這種情況,應該檢查程序代碼。     對一個對象的爭用也會引起系統性能的下降,最終會導致出現類似於掛起的狀態。這種情況下,大多數線程的狀態就是“等待監視實體”。所以應該仔細檢查程序代碼以減少對特定對象的爭用。線程可能在等待來自另一個服務器的響應,而另一個服務器可能由於其他原因也處於掛起狀態,它就無法給這個服務器發送響應信息。     在上述所有情況中,您都需要每隔幾秒取一個線程堆(總共取多個線程堆),從而能夠比較所有線程堆中的某一個線程的狀態,並且您可以決定線程可以繼續運行還是繼續等待。     JVM崩潰的情況   JVM的崩潰可能會由下列原因引起:     1.JVM庫中的bug。     2.應用程序錯誤地使用了JNI API。     3.應用程序(原生數據庫驅動等)所用的原生模塊中存在bug。     通常,當JVM崩潰的時候,它會給出引起崩潰的Java線程的堆棧跟蹤。一些JVM會在退出之前給出一個完整的線程堆(所有線程的堆棧跟蹤)。我們關心這些線程堆的目的主要在於尋找崩潰時正在運行的是哪個線程。這個線程稱為“當前線程”。如果JVM給出一個完整的線程堆,它會標出“當前線程”。因此,一般來說找出引起崩潰的線程還是比較容易的。     注意,崩潰的線程堆通常輸出到stdout或stderr。JVM可能也會生成一個二進制格式的核心文件。     崩潰線程堆的例子   如果您檢查一下清單3中的崩潰線程堆,您可以看到引起崩潰的線程的堆棧跟蹤如下所示:     Current Java thread:  at com.aaa.bbb.QQq.Direct.setString(Native Method)  at com.aaa.bbb.qqq.PreparedStatement.setString(PreparedStatement.java:51)  - locked (a com.aaa.bbb.qqq.PreparedStatement)  最後一個執行的方法(setString)是類“com.aaa.bbb.qqq.Direct”中的一個“原生方法”。因此,我們可以推測出崩潰正是由setString方法的實現中的原生代碼引起的。崩潰的原因也可以歸結為實現JNI API的JVM庫代碼有問題。     您還可以看到JVM在堆中給出了下面的信息:     UneXPected Signal : 11 occurred at PC=0x403AC9D1  Function=(null)+0x403AC9D1  Library=/opt/sunjdk/1.4.1_01-b01/jre/lib/i386/client/libjvm.so  # HotSpot Virtual Machine Error : 11  # Error ID : 4F530E43505002E6  # Please report this error at  # http://java.sun.com/cgi-bin/bugreport.cgi  這意味著引起崩潰(PC=0x403AC9D1)的代碼是JVM庫的一部分:"/opt/sunjdk/1.4.1_01-b01/jre/lib/i386/client/libjvm.so"。但這並不表示JVM一定有bug。盡管引起崩潰的指示是在JVM代碼中,但崩潰也可能是由於應用程序代碼(這裡是指原生方法setString的實現)的JNI調用引起的,應用程序代碼可能傳遞了一些錯誤的變量,從而導致JVM模塊的崩潰。     如果在堆中沒有“當前線程”的信息,那麼我們就需要分析核心堆。這一內容不在本文的討論范圍之中。




Copyright © Linux教程網 All Rights Reserved