開始之前
在本教程中可以學到什麼?如何從本教程獲得最大的收益?
關於本教程
本教程介紹 Eclipse Test and Performance Tools Platform(TPTP),逐步說明了如何安裝 Eclipse 和 TPTP 工具,並演示如何分析正在運行的 Java 應用程序。
前提條件
為了從本教程中獲益,您應該具備 Java 軟件開發經驗並了解整個軟件開發生命周期,包括測試和分析。還應該熟悉從命令行安裝軟件,以及設置和管理 shell 和系統環境變量,比如 Java CLASSPATH。了解 Eclipse 和 Standard Widget Toolkit(SWT)也是有幫助的。
在開始之前,必須在 UNIX®、Linux®、Mac OS X 或 Microsoft® Windows® 系統上安裝幾個軟件包。需要 Java 虛擬機(Java Virtual Machine,JVM)、Eclipse 平台、Eclipse TPTP 運行時以及 TPTP 所依賴的幾個軟件。還需要 TPTP 的 Agent Controller,它允許啟動並分析應用程序。下面是所需的所有軟件:
Java 2 Platform, Standard Edition(J2SE) V1.4 Software Development Kit(SDK)
J2SE V1.4 Java Runtime Environment (J2RE)
Eclipse V3.1 SDK
Eclipse Modeling Framework(EMF) SDK V2.1
XML Schema Infoset Model(XSD) SDK V2.1
V1.1.1 of UML2
TPTP 運行時
Agent Controller 運行時 (適合您系統的版本)
Eclipse Update Manager(可選)
系統需求
如果系統上沒有安裝 JVM 和 Eclipse,那麼所有軟件至少需要 300 MB 的空閒磁盤空間。還需要有足夠的空閒物理內存來運行 JVM。一般來說,建議使用 64 MB 或更多的空閒物理內存。
TPTP 簡介
在交付健壯的產品質量的程序所需的過程中,編寫應用程序或 servlet 的 Java 代碼只是第一個階段。必須對代碼進行測試,檢驗它的操作和正確性。往往還必須對代碼進行分析,以便消除性能瓶頸和資源浪費(尤其是內存)。還必須對代碼進行監視,以便對故障進行定位、識別使用模式、尋找進一步增強和優化的機會以及探測入侵嘗試和實際的入侵。
Eclipse TPTP 是什麼?
雖然存在許多對代碼進行測試、分析和監視的工具,但是很少能夠集成為一個大型的工具包。另外,這類工具通常是由不同的廠商提供的,這使您不得不花費寶貴的時間和精力來學習和掌握許多不同的用戶界面(UI)、編譯器和診斷技術。在時間和資金兩方面,專有的開發工具都需要很大的投資。
但是,如果測試工具、分析器和監視器是開放源碼的,那麼會怎麼樣?而且,如果這些工具是可擴展的,任何廠商或開發人員都可以創建新工具或者對現有工具進行改進,那麼會怎麼樣?如果這些工具能夠很好地集成到一種現有的流行的開發環境中,那麼會怎麼樣?這恐怕是白日做夢吧?不,這已經是現實了。
Eclipse TPTP 是一種軟件體系結構以及幾個擴展了 Eclipse 平台的組件(到目前為止),它在 Eclipse 平台上提供了測試、性能和監視工具。一些 Java、C 和 C++ 開發人員使用 TPTP 為其他開發人員構建獨特的工具,還有一些開發人員將 TPTP 提供的工具和 Eclipse 的其他特性組合起來,構建和部署供用戶使用的應用程序。
TPTP 提供了什麼
簡單地說,TPTP 是一個 Eclipse Foundation 頂級項目,它的目標是:“構建一個通用的可擴展的基於標准的工具平台,軟件開發人員可以在這個平台上創建專用的可互操作的...測試和性能工具。” 換句話說,TPTP 在本質上是一個工具,用來構建對軟件質量控制進行自動化的工具。
實際上,TPTP 的當前版本包括核心工具和三種核心衍生工具:
TPTPTPTP 為 UI 開發、數據收集、基於規則的數據查詢以及應用程序的控制提供了基礎代碼。例如,TPTP 提供了其他工具可以重用和擴展的許多向導。它還提供了編程接口和一個守護進程,以便幫助從正在運行的本地或遠程進程中收集數據。TPTP Testing Tools這個項目是在 TPTP 之上構建的,提供了對應用程序進行各種自動化測試所需的其他服務。當前版本支持 JUnit 自動測試、一種指向和點擊腳本編程系統(用於進行手工測試並記錄結果)和一個用於測試 Web 應用程序的自動化系統,包括一個可以記錄和回放 Web 浏覽會話並對結果進行驗證的記錄器。Eclipse V4.1 還包括一個圖形用戶界面(GUI)記錄器的早期版本,它可以記錄和回放基於 SWT 的界面中的鼠標和鍵盤事件。TPTP Monitoring Tools這個項目對來自日志文件或來自應用程序收集的統計數據的數據進行收集、分析和圖形顯示。TPTP Tracing and Profiling Tools這個項目也擴展了 TPTP,用來收集和分析正在運行的應用程序中的資源使用數據,包括 CPU 和內存。這個跟蹤工具還允許與正在運行的進程進行交互。例如,可以手工地實施垃圾收集並檢查剩余的對象池,從而尋找和修復內存 “洩漏”。
另外,TPTP 包括一個稱為 Agent Controller 的守護進程。Agent Controller 是 Eclipse 工作台和被測試的應用程序之間的 “聯絡人”。它代表 Eclipse 啟動本地或遠程 Java 應用程序並轉發應用程序度量(包括應用程序日志文件)給 Eclipse。
本教程的剩余部分演示針對 Java 技術的幾種 TPTP 分析工具。
安裝必需的軟件和組件
在開始學習本教程之前,必須安裝和設置 “前提條件” 小節中列出的軟件和組件。
安裝 J2SE 和 J2RE
下載並安裝 J2SE V1.4 SDK 和 V1.4 J2RE。(如果系統上已經有 J2SE V1.4.2_10 或更高版本,那麼可以跳過這一步。)
通常,J2SE SDK 和 Java Runtime Environment(JRE)是以自解壓的二進制文件形式發布的。Linux 上的安裝通常只需執行以下命令:
清單 1. J2SE SDK 和 Java Runtime Environment 安裝
% cd ~
% mkdir ~/java
% cd ~/java
% mv ~/j2sdk-1_4_2_10-linux-i586.bin .
% mv ~/j2re-1_4_2_10-linux-i586.bin .
% chmod +x j2sdk-1_4_2_10-linux-i586.bin j2re-1_4_2_10-linux-i586.bin
% ./j2sdk-1_4_2_10-linux-i586.bin
.
% ./j2re-1_4_2_10-linux-i586.bin
.
% ls -F
j2re1.4.2_10/ j2sdk1.4.2_10/
使用 Eclipse Update Manager 安裝 TPTP(可選)
如果已經安裝了 Eclipse,那麼可以使用 Eclipse Update Manager 安裝 TPTP。步驟如下:
點擊 Help > Software Updates > Find and Install。
選擇 Search for new features to install 選項,然後點擊 Next。
點擊 New Remote Site,然後分別輸入 TPTP Update Site 和 http://eclipse.org/tptp/updates/site.xml 作為名稱和 URL。點擊 Finish。
選擇要安裝的特性,然後點擊 Next。
接受許可協議,點擊 Next,然後點擊 Finish。
在確認提示中,點擊 Install All。當安裝完成時,重新啟動 Eclipse。
現在,可以跳過後面 “安裝 Agent Controller” 小節中描述的安裝 Agent Controller(它必須手工安裝)的步驟。如果還沒有安裝 Eclipse,請繼續閱讀下文。
安裝 Eclipse V3.1 SDK
下載適合自己平台的 Eclipse 3.1 SDK。可以在 Eclipse Downloads 上找到這個 SDK。通常,安裝時只需將 Eclipse .tar.gz 文件釋放到您選擇的目錄中。例如,如果使用 Linux,那麼下載 Eclipse V3.1 SDK tarball,然後使用以下命令將它釋放到一個目錄中,比如 ~/java/:
% cd ~/java
% mv ~/eclipse-SDK-3.1.1-linux-gtk.tar.gz .
% tar zxvf eclipse-SDK-3.1.1-linux-gtk.tar.gz
如果想檢驗 Eclipse 是否已經成功安裝了,那麼留在釋放 Eclipse 的目錄中,確保 java 可執行文件在 PATH 中並運行 java -jar eclipse/startup.jar。例如:
清單 2. 檢驗 Eclipse 是否已經成功安裝了
% export JAVA_DIR=$HOME/java
% export JAVA_HOME=$JAVA_DIR/j2sdk1.4.2_08/sdk
% export PATH=$JAVA_HOME/bin
% export CLASSPATH=$JAVA_HOME
% cd $JAVA_DIR
% java -jar eclipse/startup.jar
如果提示您為工作空間選擇目錄,那麼輸入 $HOME/java/workspace。這個目錄將保存您在 Eclipse 中創建的所有項目。(當然,如果有許多項目,以後可以選擇其他目錄,讓一個工作空間只包含一個項目。)
安裝 EMF SDK V2.1
如果 Eclipse 正在運行,就退出它並下載 EMF SDK V2.1。(根據 EMF Web 站點所說,“EMF 是一種建模框架和代碼生成設施,用於根據結構化數據模型構建工具和其他應用程序。”)在下載文件之後,進入包含 Eclipse 文件夾的目錄並運行 unzip emf-sdo-SDK-2.1.0.zip。例如:
清單 3. 運行 unzip emf-sdo-SDK-2.1.0.zip
% cd $JAVA_DIR
% ls
eclipse j2sdk1.4.2_08
% mv ~/emf-sdo-SDK-2.1.0.zip .
% unzip emf-sdo-SDK-2.1.0.zip
creating: eclipse/features/
creating: eclipse/features/org.eclipse.emf.ecore.sdo_2.1.0/
creating: eclipse/features/org.eclipse.emf_2.1.0/
inflating: ...
安裝 XSD SDK V2.1
下載 XSD SDK V2.1。(根據項目 Web 站點所說,“XSD 是一個庫,它提供了一個應用編程接口(API),用於按照 World Wide Web Consortium(W3C)XML Schema 規范的描述操作 XML 模式的組件。”)在下載文件之後,進入包含 Eclipse 目錄的目錄並運行 unzip xsd-SDK-2.1.0.zip。下面是一個例子:
% cd $JAVA_DIR
% mv ~/xsd-SDK-2.1.0.zip .
% unzip xsd-SDK-2.1.0.zip
如果提示您確認覆蓋任何文件,那麼只需按 y(小寫)對每個問題回答 Yes。
安裝 UML V2.0 Metamodel Implementation
要使用 TPTP 的 Unified Modeling Language(UML)特性,就需要安裝 UML V2.0 Metamodel Implementation。如果正在使用 Eclipse V3.1.1,那麼下載 V1.1.1 of UML2,然後在包含 Eclipse 的目錄中釋放它的存檔文件:
% cd $JAVA_DIR
% mv ~/uml2-1.1.1.zip .
% unzip uml2-1.1.1.zip
安裝 TPTP 運行時
對於下一步,下載 TPTP 運行時,這包含所有 TPTP 特性和集成兩個系統所需的 Eclipse 插件。要安裝 TPTP,進入包含 Eclipse 的目錄並運行 unzip tptp.runtime-TPTP-4.1.0.zip。下面是一個例子:
% cd $JAVA_DIR
% mv ~/tptp.runtime-TPTP-4.1.0.zip .
% unzip tptp.runtime-TPTP-4.1.0.zip
安裝 Agent Controller
Agent Controller 是 TPTP 的一個重要組件,它使 Eclipse 能夠啟動應用程序並與這些應用程序進行交互,從而提取分析數據。下載適合您的操作系統的 Agent Controller 運行時。接下來,在包含 Eclipse 的目錄中創建一個稱為 tptpd 的目錄,並將 Agent Controller 存檔文件釋放到這個目錄中。要運行的命令是:
% mkdir $JAVA_DIR/tptpd
% cd $JAVA_DIR/tptpd
% mv ~/tptpdc.linux_ia32-TPTP-4.1.0.zip .
% unzip tptpdc.linux_ia32-TPTP-4.1.0.zip
如果看到兩個下面這樣的錯誤:
linking: lib/libxerces-c.so
warning: symbolic link (lib/libxerces-c.so) failed
linking: lib/libxerces-c.so.24
warning: symbolic link (lib/libxerces-c.so.24) failed
那麼必須通過輸入以下命令來手工重新創建這兩個鏈接:
% cd $JAVA_DIR/tptpd/lib
% rm libxerces-c.so libxerces-c.so.24
% ln -s libxerces-c.so.24.0 libxerces-c.so
% ln -s libxerces-c.so.24.0 libxerces-c.so.24
添加 Agent Controller 目錄
要使用 Agent Controller,必須將它的 lib 目錄添加到 LD_LIBRARY_PATH 中。例如,如果正在運行 Linux 並采用以上步驟中給出的目錄結構,那麼用以下命令添加 $JAVA_DIR/tptpd/lib:
% export LD_LIBRARY_PATH=$JAVA_DIR/tptpd/lib:$LD_LIBRARY_PATH
還必須確保 Controller 的 lib 和 bin 目錄的內容是可執行的。為此,運行:
% chmod +x $JAVA_DIR/tptpd/{bin,lib}/*
現在將配置和啟動 Agent Controller 的腳本添加到 PATH:
% export PATH=$JAVA_DIR/tptpd/bin:$PATH
針對環境配置 Agent Controller
最後,要配置 Agent Controller 以便匹配環境。進入 Agent Controller 的 bin 目錄,然後運行 SetConfig.sh。
% cd $JAVA_DIR/tptpd/bin
% ./SetConfig.sh
當配置腳本提示您進行選擇時,接受默認設置。運行配置腳本會在 Agent Controller 的文件層次結構中創建文件 config/serviceconfig.xml。
測試 Agent Controller
為了測試 Agent Controller,運行 RAStart.sh。為了停止 Agent Controller,運行 RAStop.sh:
清單 4. 停止 Agent Controller
db% RAStart.sh
Starting Agent Controller
RAServer started successfully
% RAStop.sh
RAServer stopped, pid = 5891
RAServer stopped, pid = 5892
RAServer stopped, pid = 5893
RAServer stopped, pid = 5894
RAServer stopped, pid = 5895
RAServer stopped, pid = 5896
RAServer stopped, pid = 5897
RAServer stopped, pid = 5898
RAServer stopped, pid = 5899
RAServer stopped, pid = 5900
RAServer stopped, pid = 5901
RAServer stopped, pid = 5902
RAServer stopped, pid = 5904
RAServer stopped, pid = 5905
RAServer stopped, pid = 5906
現在完成了!重新啟動 Eclipse。在 Eclipse 工具欄上應該會看到一個新按鈕,如圖 1 所示。這是 TPTP Profile 按鈕。TPTP 已經安裝了,您可以繼續學習本教程了。
圖 1. TPTP Profile 按鈕
對 Java 應用程序進行分析
既然已經安裝了 TPTP 和底層軟件,現在就運行 Eclipse。
示例應用程序
要分析的 Java 應用程序見清單 5。
清單 5. 由少量對象組成的簡單 Java 應用程序
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class SpaceShipToy {
/*
* To build a spaceship, you need a capsule, a booster,
* three stages, and two monkeys (borrowed
* from a Barrel of Monkeys).
*/
public Capsule capsule = new Capsule();
public Booster booster = new Booster();
public Stage[] stage = new Stage[3];
public SpaceShipToy()
{
for (int i = 0; i < 3; i++)
stage[i] = new Stage();
}
private void _killTime(int seconds)
{
if (seconds <= 0)
return;
for (int i = 0; i < seconds; i++);
}
static final int MINUTE = 60;
static final int CAPSULE = 2 * MINUTE;
static final int BOOSTER = 5 * MINUTE;
static final int STAGE = 3 * MINUTE;
static final int MONKEY = 10 * MINUTE;
class Capsule {
public Monkey chimp1 = new Monkey(), chimp2 = new Monkey();
public Capsule() {
System.out.println("Start building the capsule...");
_killTime(CAPSULE);
chimp1.build();
chimp2.build();
System.out.println("Capsule complete.");
}
}
class Booster {
public Booster() {
System.out.println("Start booster...");
_killTime(BOOSTER);
System.out.println("Blast off.");
}
}
class Stage {
public Stage() {
System.out.println("start stage...");
_killTime(STAGE);
System.out.println("Stage complete.");
}
}
class Monkey {
public void start() {
System.out.println("Start the monkey business...");
}
public void build() {
start();
_killTime(MONKEY);
finish();
}
public void finish() {
System.out.println("Monkey business complete.");
}
}
public static void main(String[] args) throws java.io.IOException
{
final int NUMBERTOYS = 9;
BufferedReader in = new
BufferedReader(new InputStreamReader(System.in));
SpaceShipToy[] toys = new SpaceShipToy[NUMBERTOYS];
String input = in.readLine().trim();
System.out.println("Toy factory is up and running...");
System.out.flush();
for (int i = 0; i < NUMBERTOYS; i++)
toys[i] = null;
while (!input.equalsIgnoreCase("q")) {
if (input == null || input.length() != 1
|| !Character.isDigit(input.charAt(0))) {
System.err.println ("Unknown option. Try 0-9, q");
input = in.readLine().trim();
continue;
}
int number = Integer.valueOf(input).intValue();
if (number == 9) {
new SpaceShipToy();
System.out.println("Whoops... Lost one...");
}
else {
if (toys[number] != null) {
System.out.println("Shipping toy # " + number);
toys[number] = null;
}
else {
System.out.println("Building toy # " + number);
toys[number] = new SpaceShipToy();
}
}
input = in.readLine().trim();
}
}
}
這個 Java 應用程序很簡單:它 “構建” 玩具飛船並將它們 “發射” 到商店。每個玩具由一個 Java 對象代表,這個對象包含幾個其他對象,這些對象各自代表玩具的一個部件。每個玩具部件都要花一定的時間來構建。
為了使用這個應用程序,按 0 到 8 數字鍵來構建玩具。如果再次按同一個鍵,構建的玩具就被 “發射”,相關聯的對象被取消引用,從而可以被垃圾收集。因此,在任何時候在內存中最多可以有 9 個玩具(如果構建了所有 9 個玩具,而且都沒有發射)。
按 9 鍵會創建一個未被引用的對象,這模擬了內存洩漏。按 q(小寫)可以退出應用程序。
運行分析會話
使用 Eclipse 和 TPTP 來運行並分析 “玩具工廠”。TPTP 可以顯示活動對象的數量、執行時間等等。要使用 Eclipse 和 TPTP:
啟動 Agent Controller。
進入 Agent Controller bin 目錄,然後運行 RAStart.sh:
% cd $JAVA_DIR/tptpd/bin
% ./RAStart.sh
在 Eclipse 中,進入工作台並通過點擊 File > New > Project 創建一個新的 Java 項目。從選項列表中選擇 Java Project,然後點擊 Next。
在下一個窗口中,輸入 Toy Maker 作為 Project Name,然後點擊 Finish。標題為 “Toy Maker” 的新項目應該會出現在 Package Explorer 面板中。
在這個項目中,在 example 包中創建一個稱為 SpaceShipToy 的 Java 類。點擊 File > New > Class。
在彈出的窗口中,在 Package 域中輸入 example 並在 Name 域中輸入 SpaceShipToy。窗口中的其他設置保持不變。
點擊 Finish。現在應該會看到新類的骨架。
將清單 1 中的代碼復制到 Eclipse 類編輯器中,一定要留下 package example 聲明,但是要替換空的類定義。保存代碼以使修改持久化,這還會使 Eclipse 自動地重新編譯類的新版本。
如果要運行這個應用程序,點擊 Run > Run...,雙擊 Java Application,選擇 SpaceShipToy,然後點擊 Run。要與這個應用程序進行交互,點擊 Windows > Show View...,然後選擇 Console。
輸入 1(數字一)並按 Return。屏幕應該與圖 2 相似。
圖 2. 在 Eclipse 下運行的玩具工廠
對同一個應用程序進行分析
要對同一個應用程序進行分析:
點擊 TPTP Profile,然後選擇 Profile...。
展開 Java Application 以顯示 SpaceShipToy。注意,Profile 窗口與點擊 Run 時出現的 Run 窗口相似,但是多了一個稱為 Profile 的選項卡。
點擊 Profile 在分析器下啟動這個應用程序。如果透視圖沒有自動切換,那麼點擊 Window > Open Perspective > Other...,然後選擇 Profiling and Logging。
當 Profiling and Logging 透視圖出現時,展開 Profiling Monitor 視圖的內容,然後選擇以 <monitoring> 開頭的行。在選擇這一行時,出現一個控制台視圖。
輸入 0、1、2 和 9。屏幕應該與圖 3 相似。
圖 3. Profiling and Logging 透視圖與正在運行的應用程序
點擊 Memory Statistics 視圖,然後展開與 example 包相關聯的行。這個視圖應該與圖 4 相似。這裡有 4 個對象實例:三個代表 “構建的” 玩具,一個代表未被引用的玩具。
圖 4. 在構建三個玩具對象和一個未被引用的玩具之後的內存消耗
Memory Statistics 視圖中的列記錄了實例和內存使用統計數據:
Total Instances 反映一個類的實例總數。
Live Instances 顯示有多少個實例仍然被引用,還沒有被垃圾收集器收集。
Collected 統計垃圾收集器已經收集了多少個類實例。
Total Size (bytes) 和 Active Size (bytes) 分別顯示所有實例使用的累積內存總量(一種高水位標志)和活動實例當前使用的內存量。
結束試運行
要結束這一次試運行,切換回控制台視圖,然後再次按 0、1 和 2 數字鍵。點擊 Monitoring 視圖中的 trash can 圖標強迫實施垃圾收集。好的垃圾收集器會收集所有未被引用的對象。在任何時候,都可以通過點擊 Refresh 刷新當前透視圖中的所有視圖,見圖 5。
圖 5. Refresh 按鈕
尋找需要的數據
Profiling and Logging 透視圖提供了許多視圖,其中提供關於應用程序狀態的大量數據。要查看視圖列表,點擊 Window > Show View > Other...,然後展開 Profiling and Logging 下面的列表。這個列表應該與圖 6 相似。
圖 6. Profiling and Logging 視圖的列表
收集數據來填充視圖
根據您的目標和面對的問題,可以收集數據來填充所有這些視圖或者其中一個視圖。在前一節中,收集的惟一數據是內存統計數據,這些數據填充在 Memory Statistics 視圖中。Monitoring 視圖與圖 7 相似。
圖 7. Monitoring 視圖中的基本統計數據
最下面的展開的列表表明,當前的分析配置只包含內存使用情況的統計數據。我們來創建一個收集盡可能多信息的新的分析配置,這樣您就能夠看到分析和性能工具提供的所有度量:
點擊 TPTP Profile 按鈕中的小箭頭,然後選擇 Profile...。
在 Profile 窗口中,選擇 SpaceShipToy,然後點擊窗口底部左邊的 New。
在最右邊面板的頂部,在 Name 域中輸入 Lots of Data ,然後點擊 Apply。
點擊 Profiling 選項卡,然後點擊 Add...。
在下一個窗口中,輸入 All 作為 Profile set name 並在 Description 域中輸入 All of the data that's available。點擊 Next。
下一個面板讓您選擇要收集的數據。展開所有選擇,然後選中所有復選框,如圖 8 所示。
圖 8. 要選擇的度量
點擊 Execution Time Analysis。
選擇 Collect method CPU time information 復選框,然後選擇 Show execution flow graphical details 選項。再次點擊 Next。下一個窗口(見圖 9)允許過濾掉您不感興趣的類。
圖 9. 從分析中排除無關的類
出於這個例子的目的,點擊面板頂部的 Add... 來添加一個新的過濾器集。將這個過濾器集命名為 No Monkey Business 並點擊 OK。
點擊規則列表右邊的 Add... 在過濾器集中添加一個新規則。在 Class name 域中輸入 example.SpaceShipToy 並在 Method name 域中輸入 main(星號),選擇 EXCLUDE 作為 Rule(見圖 10),然後點擊 OK。
圖 10. 編輯過濾器規則
在更大的窗口中,點擊 Apply。
現在已經定義了要分析的應用程序和要收集的度量。點擊 Profile。
在 Profiling and Logging 透視圖中,點擊 Memory Statistics 視圖。應該會看到,根據剛才創建的規則,沒有對方法 example.SpaceShipToy.main() 執行度量。
提示和技巧
TPTP 工具提供了豐富的特性,可以幫助您深入地了解應用程序。另外,因為 TPTP 與 Eclipse 的其余部分很好地集成在一起,您會發現許多方便之處。
為了幫助您,下面提供一些提示和技巧。
快速建立數據過濾器
啟動一個分析會話並打開 Execution Statistics 視圖。這個視圖顯示一個應用程序中的所有類和方法(至少是還沒有在過濾器集中過濾掉的那些)。在這個視圖中右擊,打開圖 11 所示的快捷菜單。
圖 11. 快捷菜單
點擊 Filter out...,選擇 Profiling,然後點擊 OK。在下一個窗口(見圖 12)中,輸入 Monkey Business 作為過濾器名並輸入 build 作為過濾器字符串,然後選擇 Filter by 下面的 Method name 選項。點擊 OK 並再次點擊 OK。
圖 12. Edit Filter 窗口
視圖應該會改為只顯示 example.SpaceShipToy$Monkey.build() 方法的統計數據。
要想恢復視圖,再次看到所有方法,可以點擊 Filter(見圖 13)並選擇 No filter。要想編輯任何過濾器,可以點擊同一個按鈕並選擇 Manage filters...。
圖 13. Filter 按鈕
跳到源代碼
仍然留在 Execution Statistics 視圖中。雙擊 SpaceShipToy$Booster 類。這時就會切換到 Java 透視圖並直接跳到內部類 Booster 的定義。內存分析視圖和 Eclipse 代碼編輯器之間的這種連接是非常有價值的特性。
高級的復雜過濾器
可以使用 Edit Filter 窗口的 Advanced 選項卡來構建任意復雜的過濾器。高級過濾器 是一系列規則,其中每個規則列出一個屬性、一個條件(比如 “equal”、“not equal” 和 “like”)和一個值。您想知道一個實例消耗了多少內存嗎?這可以用高級過濾器來表達。
組織和排序
大多數數據視圖可以按照包、類和方法對數據進行組織。通過點擊大多數數據視圖頂部的三個按鈕之一(見圖 14),可以快速地改變數據的組織方式。
圖 14. 用於組織數據的三個按鈕
從左到右,分別是按照包、類和方法進行組織的按鈕。
還可以通過點擊任何列標題,對所有數據進行排序。點擊列標題一次,就對這個列按照升序進行排序;再次點擊同一個列標題,就按照降序顯示數據。
使用 UML2 序列圖深入了解代碼
TPTP 提供了另一個對了解正在運行的應用程序有幫助的特性:UML2 序列圖。這個圖不但顯示類和方法之間的調用序列,還可以突出顯示熱點,也就是消耗了大部分應用程序執行時間的代碼部分。
在序列圖中查看代碼
要使用 UML2 序列圖查看代碼:
退出正在分析的所有玩具工廠應用程序實例。
點擊 TPTP Profiling 按鈕中的小箭頭。選擇 Profile...,選擇 SpaceShipToy,點擊 Profiling 選項,然後選擇 All。
點擊 Apply(如果需要的話),然後點擊 Profile。
當應用程序在 Profiling Monitor 視圖中啟動時,右擊剛啟動的進程並選擇 Open With... > UML2 Thread Interactions 來打開 UML2 class Interactions 視圖。因為這個應用程序剛剛啟動,所以這個圖只顯示了對 main() 的調用,如圖 15 所示。
圖 15. 只顯示 main() 調用的簡單 UML 圖
切換到控制台視圖並與應用程序進行交互,從而產生更多的方法調用。然後切換回 UML2 Trace Interactions 視圖。UML 圖應該已經增大了,可能與圖 16 相似。
圖 16. 示例應用程序的 UML2 視圖
將鼠標指針放在左邊空白處中的紅色條上,就會顯示一個表示 CPU 時間消耗的刻度條(見圖 17)。
圖 17. CPU 消耗刻度條
深紅色表示最大。在這個應用程序中,SpaceShipToy 類的構造過程顯然是瓶頸。
對對象引用進行編目
另一個對正在運行的應用程序進行分析的有用特性是 Object References 視圖,這個視圖顯示應用程序中每種對象的引用數量。如果您的 Java 代碼看起來消耗了過多的內存,那麼收集並查看對象引用可以幫助您找到出問題的代碼。
使用 Object References 視圖
使用 Object References 視圖與使用其他 TPTP 視圖一樣容易:
終止正在分析的所有進程。
在 Profiling Monitor 中右擊並選擇 Unload Profiling Data。按照提示刪除到目前為止收集的所有數據。
選擇以前監視的所有進程並按 Delete。出現提示窗口時,選擇 Do not delete contents,然後點擊 Yes。
對玩具工廠應用程序啟動一個新的分析會話。切換到控制台視圖,然後按 1 2 9 來創建兩個玩具和未被引用的第三個 “玩具”。
通過在 Profiling Monitor 視圖中點擊 Collect object references,收集正在使用的對象(見圖 18)。
圖 18. Collect Object References 按鈕
在 Eclipse 工具欄上,點擊 Open Object References(見圖 19)來打開 Object References 視圖。Object References 視圖列出應用程序中使用的每個對象和對應的引用數量。
圖 19. Object References 按鈕
展開 SpaceShipToy。引用表應該與圖 20 相似。已經創建了三個玩具,所以這個表是准確的,因為每個玩具包含一個推進器、一個太空倉(其中有兩只猴子)和三級火箭。
圖 20. 示例應用程序在某一時刻的 Object References 視圖
對本地 Java 應用程序使用 TPTP
除了對通過 Eclipse 工作台構建和啟動的 Java 應用程序進行分析之外,還可以分析本地計算機上已經在運行的 Java 應用程序(應用程序也可以在遠程計算機上,但是這裡不討論分析遠程應用程序的過程)。可以通過 TPTP 附著 到進程上。附著之後,就可以使用前面學習過的所有工具來探索和觀察內存分配。
附著到本地 Java 應用程序
要附著到本地 Java 應用程序:
退出和刪除 Profiling Monitor 視圖中所有正在運行的進程。
打開一個命令窗口並確認 PATH 和 JAVA_HOME 環境變量指向 JRE。
確保 LD_LIBRARY_PATH 包含 TPTP 庫。在測試系統上,這些變量如下所示(根據您的系統配置,路徑名和目錄名可能不一樣):
% printenv
.
PATH=/bin:/usr/bin:/usr/X11R6/bin:/home/mstreicher/java/jdk/bin
JAVA_HOME=/home/mstreicher/java/jdk
JAVA_DIR=/home/mstreicher/java
LD_LIBRARY_PATH=/home/mstreicher/java/tptpd/lib:
確保 TPTP Agent Controller 正在運行。進入包含 Agent Controller 的目錄,然後停止並重新啟動這個守護進程:
% cd $JAVA_DIR/tptpd/bin
% ./RAStop.sh
RAServer stopped, pid = 3163
RAServer stopped, pid = 3164
.
RAServer stopped, pid = 18108
% ./RAStart.sh
Starting Agent Controller
RAServer started successfully
將玩具工廠應用程序作為單獨的獨立 Java 應用程序運行:
進入包含示例應用程序的工作空間目錄,可能是 $HOME/workspace/Toy Maker。在這個目錄中,應該會看到一個稱為 example 的目錄,其中包含 SpaceShipToy.java 的代碼和相關聯的 .class 文件。
將當前工作目錄(.(點號))添加到 CLASSPATH:
% cd $HOME/workspace/Toy Maker
% ls -F
example
% export CLASSPATH=.:$CLASSPATH
用 -XrunpiAgent:server=enabled 選項通過 TPTP Agent Controller 啟動應用程序。enabled 模式以普通方式啟動 Java 應用程序並在後台運行控制器。(如果在附著到應用程序之前不想執行它,那麼使用 controlled 模式。)
% java -XrunpiAgent:server=enabled example/SpaceShipToy
返回到 Eclipse,然後點擊 TPTP Profile 按鈕中的小箭頭。選擇 Profile...。在左邊的列表中,雙擊 Attach - Java Process。
在下一個窗口中,在 Name 域中輸入 Toys,然後選擇 localhost 作為主機。點擊 Apply。
通過點擊 Agents 選項卡選擇一個代理,如圖 21 所示。
圖 21. 選擇要附著的代理
點擊顯示的代理(如果當前沒有在分析其他應用程序或進程,那麼應該有一個代理),然後點擊 >。為了保存這一修改,再次點擊 Apply。
通過點擊 Profiling 選項卡並選擇一個分析集,從而選擇要收集什麼數據。可以選擇 All,就會采用前面創建的分析集。
點擊 Apply,然後點擊 Profile。Eclipse 應該會切換到 Profiling and Logging 透視圖。
在大多數系統上,這時會出現與圖 22 相似的窗口。
圖 22. Profiling 提示
這個警告是正常的,它提醒您盡管已經附著到了這個進程,但是必須手工啟動監視。為了啟動監視,右擊剛啟動的分析進程,然後選擇 Start monitoring。
切換回命令窗口,與應用程序進行交互。再返回 Eclipse 查看正在運行的應用程序的分析數據。完成之後,點擊 Terminate Process 按鈕(見圖 23)。
圖 23. Terminate Process 按鈕
將在命令窗口中殺死進程:
% java -XrunpiAgent:server=enabled example/SpaceShipToy
.
Stage complete.
Start stage...
Stage complete.
Start stage...
Stage complete.
zsh: killed java -XrunpiAgent:server=enabled example/SpaceShipToy
%
結束語
本教程演示了如何通過分析代碼來調整和改進 Java 應用程序。通過使用 Eclipse TPTP,可以運行代碼並探測哪些代碼段浪費了內存並導致性能降低。TPTP 還提供了其他工具,可以從大型日志文件中提取信息,以及自動地獲取和分析測試結果。
TPTP 中的其他工具包括一個記錄-回放設施(可以測試在 Eclipse 下運行的應用程序的 GUI)和一個稱為 XRay 的特殊分析器(專門用於分析源自 Eclipse 平台的應用程序)。