使用中有何問題,請隨時與Borland Technical Support聯(lián)系。
測試
java
程序
Optimizeit Thread Debugger是從運行程序的虛擬機中收集程序的覆蓋信息的。
首先要運行一個java程序,您需要安裝一個java虛擬機。Optimizeit Thread Debugger默認安裝配置JDK 1.4 or 1.4.1。如果你想使用另外的虛擬機,可以查看Optimizeit Thread Debugger用戶手冊的如何增加新的虛擬機部分。從運行程序的虛擬機中收集程序的覆蓋信息的。
首先要運行一個java程序,您需要安裝一個java虛擬機。Optimizeit Thread Debugger默認安裝配置JDK 1.4 or 1.4.1。如果你想使用另外的虛擬機,可以查看Optimizeit Thread Debugger用戶手冊的如何增加新的虛擬機部分。
Optimizeit Thread Debugger可以測試任何類型的java程序,比如標準應用程序,應用小程序, servlets, JSPs, EJBs。本文所使用的測試程序包含在\doc\thread_debugger\quicktour目錄下。所有的測試都是在Optimizeit Thread Debugger中進行的:
- 打開Optimizeit Thread Debugge。
- 如果你是第一次打開,將會自動彈出編輯設置窗口。如果已經(jīng)打開,可以從file菜單下選擇new setting,調(diào)出編輯設置窗口。
- 在程序類型框中選擇Application。
- 單擊"Program main class or Jar file"右面的“Browse…”按鈕。
- 找到\doc\thread_debugger\quicktour\ThreadDExample.class文件,然后單擊open。
- Optimizeit Thread Debugger會返回到設置窗口,并且自動帶入程序的工作區(qū)和類路徑。在Source Path框中,單擊change…按鈕。
- 在Source path chooser窗口中,選擇安裝路徑下的\doc\thread_debugger\quicktour目錄;選中后單擊窗口中的向下按鈕把它加入到source path部分。
- 單擊ok增加到源文件中路徑中。設置好后的對話框如下:

- 選中Open a console選項;這是非常重要的,因為這個程序需要輸入一些命令來運行。
- 單擊Start now按鈕。
- 編輯設置窗口會自動關(guān)閉, Optimizeit Thread Debugger會啟動被測程序。
?
Thread Debugger默認打開的是線程監(jiān)視窗口。你也可以重新定義默認打開的窗口來查找程序在哪里掛起了或者是比較耗時:
- 程序啟動后, Optimizeit Thread Debugger打開了一個DOS窗口,在DOS窗口中輸入1,然后按回車鍵開始第一輪測試。Thread Debugger窗口中會顯示當前運行的線程和他們各自的運行狀態(tài):

整個過程中窗口中會使用不同的顏色條來顯示當前的線程運行狀態(tài):
- 運行狀態(tài) (綠色)表示線程正在運行并使用CPU 。
- 堵塞狀態(tài) (黃色)表示該線程沒有運行,因為在進入monitor時發(fā)生了阻塞。
- 等待狀態(tài)(紅色)表示線程正在等待monitor的通知與其他線程共享資源。
- 等待輸入輸出狀態(tài) (紫色)表示該線程的本地代碼沒有執(zhí)行任何的過程。這常常是由于一個輸入/輸出操作操作引起的,但也有可能是其它情況導致給代碼沒有分配使用CPU而造成的。
注意:當相應的線程狀態(tài)應該被更新而未更新時,Optimizeit會在顏色條中顯示一些點。當測試程序退出或沒有收集到信息時,未知狀態(tài)的區(qū)域也將以不同類型的圖案來顯示。
- 先選中主線程"main",然后單擊I/O-Waiting View 切換到等待I/O監(jiān)視窗口:
.gif)
- 雙擊第三行,可以從源代碼窗口中看到發(fā)生等待的那行代碼。該程序基本上都使用了標準的輸入來方法。關(guān)閉源碼窗口并返回到線程活動監(jiān)視窗口。
- 在線程窗口中也可以選取某一段來分析該時間內(nèi)的一些信息。返回到控制窗口并且輸入2,然后單擊回車啟動第二輪測試(使用了小的連接)。Thread Debugger顯示了當前正在運行的線程的狀態(tài)。你會看到在開始的某段時間內(nèi)有10個線程在運行。
- 在線程活動監(jiān)視窗口,拖動滾動條到剛才的這段時間范圍內(nèi)。如果不想繼續(xù)查看線程活動信息,可以取消選中窗口右下方的Update continuously選項,這樣窗口中的信息就不會隨時間變化了。
- 在這段時間內(nèi)黃色區(qū)域表示相應的線程堵塞在一個monitor中。在線程條上直接單擊黃色條上任意一部分,并按住鼠標左鍵向右移動。這樣就選中了該時間段:

選中時間段后:
- 單擊Contention View 來分析為什么線程在進入monitors時會發(fā)生阻塞。該信息表示的僅是選擇時間段的。
- 單擊Waiting View 來研究線程在哪里等待monitors。同樣,該信息也僅僅是所選時間范圍的,所以應選擇圖中紅色條部分的區(qū)域。
注意:如果線程已經(jīng)退出或者已不存在該線程名在窗口中就會以斜體顯示。
理解線程爭用
在java多線程的應用程序中如果許多線程同時需要同樣的monitors(可譯為監(jiān)視器或管程),就會造成嚴重的性能瓶頸。Monitors是被用來保護共享資源被多個線程同時調(diào)用的,每一個對象都有一個monitors,同時只允許一個線程持有monitors從而進行對對象的訪問,那些沒有獲得monitors的線程必須等待,直到持有monitors的線程釋放monitors。這部分被monitors保護起來的代碼,我們稱之為臨界區(qū)。
為了優(yōu)化性能,我們經(jīng)常要盡可能地保持小的臨界區(qū),特別是當臨界區(qū)在執(zhí)行一些不耗用CPU資源的過程時。如果一個線程在臨界區(qū)域內(nèi)由于等待輸入輸出而造成等待,它就不再使用CPU資源。然而,如果其他的線程也等待這個monitors的話就就不能充分利用CPU資源。
下面我們用實例來說明這個問題:
- 重新啟動被測試程序。
- 程序啟動后,在DOS窗口中輸入1并按下回車鍵啟動爭用較大的示例。你會注意到在某段時間內(nèi)有10個線程在運行。在線程窗口,拖動窗口下面的滾動條返回到這段時間,并且取消選中"Update continuously":

- 可以看到此例中相對于實際使用CPU的時間來說爭用monitor所花費的時間更長。為了更全面地了解這個情況,選中任何線程(例如線程7)并且單擊Contention View 切換到爭用查看窗口:

- 由于爭用ContentionExample$Consumer.run()方法100%的時間花在了堵塞上,爭用監(jiān)視控制臺上顯示了所有與該爭用有關(guān)的monitor。選中java.lang.Object monitor。
- 爭用詳細說明控制臺就會顯示列出所有與該爭用有關(guān)的線程,包括什么時候發(fā)生了爭用。打開第一個線程:

- 在ContentionExample$Consumer.run()行上雙擊就會彈出該方法的源代碼。在源碼窗口顯示線程7正在獲取monitor,然而源碼窗口下方顯示線程1同時也在獲取monitor:

在被monitor“l(fā)ock”保護的臨界部分,此例中調(diào)用了方法processData()。該方法用1毫秒來模擬輸入輸出。
此例說明了使用過多的爭用會付出很大代價。對于使用幾毫秒cpu資源來說, 每個線程大概阻塞1秒。為了降低爭用的發(fā)生,當確認臨界區(qū)不再等待爭用時,有必要使臨界區(qū)最小化。
- 我們的解決辦法很簡單,就是通過排除不需要同步處理的數(shù)據(jù)來處理縮小臨界區(qū)。這樣代碼調(diào)整為:
synchronized (lock) {
? a = dataSourceA.nextElement();
? b = dataSourceB.nextElement();
}
processData(a, b);
- 演示程序中也包含了測試useBigLock 標志的情況。切換到線程監(jiān)視窗口。
- 重啟被測程序,在DOS窗口中輸入2 后按下回車鍵運行第二個測試。第二個測試也使用的同樣的代碼,但是這次useBigLock 標記被置為false。爭用時間降低到了每個線程只有幾毫秒。
這里有一些技巧來防止由于爭用而引起的性能問題:
- 盡量使臨界區(qū)最小。僅包含需要被保護的語句。
- 鎖定可能最低的級別。
- 在synchronized方法前加上explicit synchronized()語句。同步方法使用起來很方便,但是也可能導致過多的爭用,僅僅因為隨著時間的增長方法趨向于復雜化。
- 不要在臨界區(qū)執(zhí)行一個I/O操作,除非唯一的目的是為了保護I/O描述符或者對象經(jīng)常執(zhí)行I/O操作。
- 猜測爭用的級別幾乎是不可能的。通過監(jiān)視線程窗口,每次重大的變化都會看到有難以理解的爭用級別。
理解死鎖
多線程的應用程序所面對的共同問題是,當一些線程由于不能獲得其所需的資源致使線程被掛起。死鎖對堆棧來說是極大的挑戰(zhàn),因為它經(jīng)常很少出現(xiàn)甚至不能復現(xiàn)。一個極少出現(xiàn)死鎖也有可能導致一個web應用程序長時間被掛起,并引起資源浪費。
Optimizeit Thread Debugger提供了兩個強有力的特征使調(diào)試死鎖變得容易。第一是給開發(fā)人員提供死鎖發(fā)生的位置。第二個特征將在下一部分來說明,是幫助開發(fā)人員來監(jiān)視應用程序的運行,并且一旦有可能發(fā)生死鎖會及時給出提示。
- 單擊Monitor View 按鈕切換到顯示執(zhí)行臨界部區(qū)所有的線程和監(jiān)視器的窗口。默認情況下該窗口的內(nèi)容是實時顯示并每秒都更新的。
- 如果有必要,重啟被測試程序并在DOS窗口種輸入3然后回車來運行一個死鎖的例子。這個死鎖是由于一個同步的參數(shù)錯誤引起的:

- 單擊圖中的任一個按鈕,相應的棧軌跡就會顯示在窗口的下方。可以雙擊某一行來查看相應的源碼。線程顏色所表示的狀態(tài)與線程窗口中所表示的一樣。
- 一旦單擊連接圖中的一個按鈕,監(jiān)視內(nèi)容就不再更新。可以再次單擊該按鈕或者重新進入該窗口圖就可以繼續(xù)保持更新。
由于重現(xiàn)和理解死鎖是比較困難的,基于這種情況, Optimizeit Thread Debugger提供了一個線程分析器。當一個java程序運行時線程分析器記錄并監(jiān)視所有的同步活動。然后搜索任何一個有可能導致死鎖的類型并且給出一個警告或者錯誤信息的列表,包含臨界區(qū)的詳細資料,諸如哪里會有有可能發(fā)生死鎖和調(diào)用了那個線程。
Optimizeit Thread Debugger會注意監(jiān)視以下一些表現(xiàn)異常的鎖類型:
- 鎖的順序:兩個線程直接或者連續(xù)地以不同的順序進入同一個monitors。
- 鎖和等待:線程進入一個monitors然后等待進入其他的monitors。
- 鎖和I/O等待:線程進入一個monitors然后停止執(zhí)行任何的任務。
除非兩個線程永遠不會同時運行,鎖的順序問題經(jīng)常預示著有被鎖死的風險。對Lock-and-wait和Lock-and-I/O-wait類型的鎖,預示著情況比較正常,如果監(jiān)視器的目的是限制對正在等待或者正在執(zhí)行輸入輸出的資源的訪問。
為了證實會發(fā)生死鎖,本文所指的示例程序中會有3個線程會同時進入同一個監(jiān)視器來獲取數(shù)據(jù)。每個線程都使用了隨機函數(shù)調(diào)用,這樣有時它們會以不同的順序進入監(jiān)視器。這個程序僅僅用來演示,然而可以細想當多個線程在一個大的應用程序中運行并且當處理一些臨界區(qū)時以不同的順序進入監(jiān)視器會發(fā)生什么樣的情況。
本例在執(zhí)行過程中經(jīng)常會發(fā)生死鎖,但是無論是否真正地發(fā)生死鎖Optimizeit Thread Debugger分析器都將匯報一些警告信息和發(fā)生的錯誤信息:
- 如有必要請重新打開示例程序。
- 單擊Monitor Usage Analyzer 按鈕。
- 單擊Record 開始記錄測試過程。
- 在程序啟動后會彈出一個DOS窗口,在窗口中輸入4并按回車鍵開始程序的運行。
注意:分析器也可以通過選擇編輯設置窗口中的"start analyzer"選項來啟動。
- 當以一定的順序發(fā)生阻塞時窗口中顯示符號 '>',以另外的順序發(fā)生阻塞時顯示符號'<'。最后,程序會停止,但是再不會出現(xiàn)菜單。這意味著發(fā)生了死鎖。按下回車鍵可以重新顯示菜單:

- 再次單擊Record停止記錄。
- 選擇第一條錯誤信息并切換到分析窗口:

- 在方法名上單擊調(diào)出源代碼。關(guān)閉源碼窗口返回到監(jiān)視器使用分析窗口。
這里有一些避免產(chǎn)生死鎖的技巧:
- 盡可能讓鎖簡單。兩個線程使用一個monitors來共享資源不會發(fā)生死鎖,同樣他們永遠不會在臨界區(qū)期間發(fā)生阻塞。
- 在進入monitors后不要執(zhí)行輸入輸出操作,除非該監(jiān)視器是用于保護輸入輸出的。
- 當進入另一個monitors時不要等待另一個monitors,除非完全有必要并且是可理解的。
- 總是以同樣的順序進入monitors。
- 當持有一個鎖時不要調(diào)用公共的synchronized方法。
活鎖過多
在多線程的應用程序中另一類典型的問題是有過多的鎖。當虛擬機被優(yōu)化后,能夠非常快地進入多個monitors,減少monitors的使用就可以極大地提高性能。
Optimizeit Profiler能夠用來度量每個方法使用了多長時間,包括進入monitors的時間,而Optimizeit Thread Debugger可使開發(fā)人員了解監(jiān)視器使用的頻率和位置,無論爭用是否發(fā)生過:
在線程窗口選擇一個線程或一個時間范圍后單擊Monitor Enter View 按鈕切換到進入monitors詳細信息查看窗口。例如,在選擇main類后單擊出現(xiàn)如下所示的窗口:

可以看到main類進入了22個monitor。
?
本文只是Optimizeit Thread Debugger的一個概覽。如果想要了解更多有關(guān)Optimizeit的信息,請查看Optimizeit Thread Debugger用戶指南。