程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 用Runtime Syp調整Eclipse的啟動性能,第2部分 - 成功的騙局

用Runtime Syp調整Eclipse的啟動性能,第2部分 - 成功的騙局

編輯:關於JAVA

Runtime Spy 是 Eclipse.org 提供的核心工具 (Core Tools)之一,它是特 別設計的一個透視圖及一組視圖,用於幫助您找到並診斷插件啟動性能問題。其 中的一個案例研究說明了Runtime Spy 如何用於提高 IBM WebSphere Studio Application Developer 的啟動性能。上一篇文章,也就是 第1 部分,對 Runtime Spy 進行了介紹。

閱讀完本系列文章的 第 1 部分 後,您應該已經對 Runtime Spy 如何來幫 助您查找啟動問題的位置有了大體的認識。讓我們通過一些特定的例子來弄明白 如何用它減少您的插件啟動時間。為了讓話題更有可讀性,我們將探討一些在 Runtime Spy 幫助下糾正的問題,這些問題的糾正是 IBM WebSphere Studio Application Developer 性能提高的一個方面。

注意:核心工具只能運行於 Eclipse 版本 2.x。在本文發表時,它們還不能 運行於 Eclipse 3.0 驅動程序上;編號為 47518 的 bug 描述了這一問題。

幫助 Eclipse 快速啟動

為提高一個基於 Eclipse 的應用程序的啟動性能,一般來說有兩個目標:

盡可能地延遲插件的激活。

在激活您的插件時將涉及到的工作量減到最少。

這兩個目標共同的原則是 盡可能延遲代碼的執行。您可以采用的一些方法:

不要加載您的插件

怎麼辦?首先,要遵循 Eclipse 本身的插件擴展的思 想。不要忘記,很多 Eclipse 擴展點定義要求貢獻者靜態地聲明足夠的信息, 以延遲代碼的加載,直到需要執行被請求的動作時才加載。這是 Eclipse 體系 結構的基本思想,在插件清單文件的聲明中得到了體現。您自己的擴展點定義也 應該采用這一方法。

減少插件初始化時加載的類的數量

最常出現問題的地方是 Plugin.startup 方法中的引用。很多插件重載這個方法以完成它們的初始化。理想的解決方法通常是,讓您的插件延遲它的初始化,直到用戶請求您的產品的 某個特定的動作時再初始化。要不然,下一個最好的選擇是最小化引用的類和插 件的數量。在任何一種情況下,Runtime Spy 都可以指出哪裡可能會占用太多的 時間或者觸發太多其他插件的激活。

在插件初始化期間減少 CPU 利用率

同樣,最常出現問題的地方還是插件 的 startup 方法的代碼或者它所調用的代碼。內存結構的滯後初始化可以節省 CPU 時間並延遲其他插件的激活。還有一種可能是在啟動時派生一個單獨的低優 先級的線程,當系統空閒時再去完成初始化,不過這種方法需要特別注意處理好 同步。

通過延遲插件的激活,會給用戶一種產品更為靈活的印象。盡管 累積的CPU 時間是完全相同的,但是相對於在最開始強制完成,將其分解為若干小塊在較長 的一段時間內分步完成會進一步降低人們的注意力。在第一次調用時(也就是啟 動您的產品或者打開第一個透視圖、編輯器或視圖時)尤其不應該有延遲,因為 正是在這個時候用戶最關注於工作的完成而沒有太多耐心。

再介紹 Runtime Spy

Runtime Spy 為您提供了用於跟蹤三種加速啟動方法的基本統計表,如圖 1 所示。

圖 1. Runtime Spy 透視圖由 Activated Plugins、Loaded Classes、 Plugin Datasheet、和 Stack Trace 四個視圖組成

您的第一個目標是讓 Activated Plugins視圖中出現的條目減到最少。對於 那些出現在列表中的插件,您的第二個目標應該是讓 Loaded Classes視圖中出 現的條目減到最少。Activated Plugins 視圖中的 Startup time列將為您指出 那些啟動時間較長的插件。在 Activated Plugins 中選擇 按鈕可以更新 Stack Trace視圖,讓您明白 插件 為何被加載,在 Loaded Classes 視圖中選擇 按鈕將為您說明 類為何被加載。

偵探 WebSphere Studio

讓我們以一個如何使用 Runtime Spy 來診斷和糾正啟動代碼錯誤的例子來開 始。如您將看到的,這些修正不會在絕對意義上“提高”啟動性能,而是延遲了 啟動開銷,這樣會讓用戶感覺產品整體上更快。底層的 Eclipse 產品在這一點 上做得非常好,它會隨著您的使用而將啟動開銷分解為小塊分階段進行。這是 Eclipse 的延遲插件加載策略的基礎。

為對情況進行第一次觀察,讓我們啟動 Studio 時只打開 Runtime Spy 透視 圖。這樣應該會加載幾乎最少的插件並以最快的速度啟動,如圖 2 所示。

圖 2. 啟動時激活最少數目的插件

提示:快速查看新激活的插件

按下 按鈕不會改變 Activated Plugin 列表中的已作出的選擇。如果您想 查看特定的動作激活了哪些插件:

在列表中選擇所有條目 (Ctrl+A)。

執行您期望激活一個或更多插件的動作。

選擇 按鈕來更新列表。

沒有被選中的條目就是新激活的插件;被選中的那些是在動作之前就已經被 激活的。另外一種方法是,您可以記下 Order列中最後加載的插件的編號,執行 動作,然後對 Order 列重新排序,查看哪些插件出現於先前(已經激活)的最 後一個插件之後。

注意插件名末尾的星號。這些插件是在負責啟動 Eclipse 的插件被激活時加 載的,那個插件名為 application plug-in。這個插件提供了一個實現 org.eclipse.core.runtime.applications 擴展點的 IPlatformRunnable 接口 的類。默認地,Workbench UI 插件提供對這個接口的一個實現,來創建工作台 窗口,收集對主菜單欄、工具欄等等的動作貢獻 (action contributions),一 般情況下還要為事務處理准備好 Eclipse 工作台用戶界面。

實際上消耗的時鐘時間將會比 Activated Plugins 視圖中顯示的 Startup time 列的和要多。這是因為後者不包括 Platform Runtime 加載之前 JVM 的運 行時間或者插件啟動之外的 CPU 運行時間。在圖 2 所示的例子中,從啟動配置 啟動 Run-time Workbench 時起實際上的啟動時間大約是 13 秒。其中還包括啟 動配置本身為構建插件列表等任務而引入的程序調試時間 (development-time) 開銷。

即使加上這些開銷,啟動時間還是非常少,不是嗎?

Workbench 啟動擴展點的結果

讓我們按下 按鈕來更新活動的插件列表。就是那樣!圖 3 中是我們的第一個困 惑。

圖 3. org.eclipse.ui.startup 擴展點激活的插件

被選中的那些插件是初始化時激活的。那麼另外 11 個插件呢?或許有一些 是因 Runtime Spy 透視圖而加載的。更為有趣的是,圖 3 中有一些看起來可疑 的 Studio 插件。列表頂部顯示的是“cheatsheet”和“internet”。它們是什 麼?為什麼現在被加載?回到用戶界面,我們在 Help下拉菜單下找到了與第一 個插件相關的選項,如圖 4 所示。

圖 4. org.eclipse.ui.startup 激活的插件可以不被激活嗎?

一般只有新用戶才會去選擇 Cheat Sheets菜單選項,所以我們為什麼還要在 啟動時為加載它們的相關插件而付出代價呢? Workbench > Startup首選參 數頁提示了答案,如圖 5 所示。

圖 5. 為 org.eclipse.ui.startup 擴展點作出貢獻的插件

org.eclipse.ui.startup 擴展點可能是版本 2.0 一直有爭議的 API 之一。當工作台窗口打開時它可以全權要求 Workbench UI 激活插件,而不受延遲加載 策略限制。如果一個插件的清單定義了這一擴展點,那麼它的插件類必須實現 IStartup.earlyStartup 方法。

有對這個 API 的合法使用。由於文章長度有限,不能盡述這個列表中每一個 條目。所以讓我們來考慮如上面圖 5 所示添加的兩個插件的合法性:

Cheat Sheets. cheat sheel 菜單選項是基於安裝的特性而動態構建的,在 每一次選擇後會重新排序。沒有 Workbench UI 方法來通過擴展實現這一行為。看起來這個插件別無選擇,只好繼續“cheat”下去。

Internet Preferences. 這一啟動代碼基於 URL 類的系統屬性初始化 Window > Preferences > Internet設置。由於沒有辦法可以知道這個類 什麼時候會被引用,並且因為沒有顯式的初始化方法,這樣做看起來是合法的。不過,考慮使用庫擴展可能是明智的。我們稍後將回來解釋原因。

首先您可能會疑惑為何 Workbench 會定義一個 Startup 首選參數頁。可以 取消選擇列出的插件之一,讓用戶有機會選擇用減少的功能換取更快速的啟動。例如,有經驗的開發者不需要 Cheat Sheets 層疊菜單提供的提示,可以選擇禁 用它對 org.eclipse.ui.startup 擴展點的貢獻,這樣就刪除了 Help > Cheat Sheets 菜單選項。如果您自己的插件對這個擴展點有貢獻,那麼要記住 這一點。也就是說,要保守地編寫您的插件代碼,假定插件類的 IStartup.earlyStartup 方法可能還沒有被調用。

回到我們在圖 3 中的例子,考慮列表中沒有星號的插件 com.ibm.etools.internet 。圖 6 中它的棧記錄確定了為什麼這個插件在工作 台窗口打開後被激活。

圖 6. 工作台啟動處理

Workbench 的 run 方法被高亮了,對擴展點的貢獻在這裡被處理。自此以後 激活的插件或者是對 startup 擴展點有貢獻,或者是對其中的引用有貢獻。

找出可能的 WebSphere Studio 熱點

讓我們打開 J2EE 透視圖來查看一些 Studio 特定內容。(如果您想診斷一 個簡單些的情形,那麼打開一個單一的視圖,比如 Window > Show View > DB Servers,而不要打開相應的透視圖;那樣已激活插件的列表會短很多 。)打開 J2EE 透視圖後,回到 Runtime Spy 並選擇 Activated Plugin 的 按鈕。哇!列表中的插件數從 22 跳到了 73,增加了 20 秒的插件 激活時間(先前總的時間也只有三秒多)。圖 7 的顯示中以插件的激活順序對 它們進行排序,最近激活的插件顯示於頂部。

圖 7. 打開 J2EE 透視圖後激活的插件

公平地講,Runtime Spy 增加了開銷,尤其是當需要捕獲棧記錄時,所以從 半熱態啟動 (warm start) 時,實際上不加修正 (uninstrumented) 消耗的總的 時間接近 37 秒。但是讓我們來看一下是否有一些啟動插件之外的插件有可能不 必啟動。為節省空間,下面這個列表並不完全,因為我們認為基本的組件比如 EMF、JDT 和 J2EE UI 是需要的。但是這些呢?

com.ibm.etools.validation.* (可以被延遲或者設為可選?)

com.ibm.etools.rsc.core.ui (db)

com.ibm.etools.rdblib (db)

com.ibm.etools.sqlmodel.* (db)

com.ibm.sed.preferences (可以被延遲?)

com.ibm.etools.rsc (db)

com.ibm.etools.sqlparse (db)

com.ibm.etools.rdbschemagen.ui (db)

com.ibm.etools.rdbexport.ui (db)

com.ibm.etools.sqlbuilder (db)

com.ibm.etools.subuilder (db 並且需要 1828ms 加載時間!)

com.ibm.etools.sqlj (db)

到現在並沒有與數據庫相關的視圖或者編輯器被打開。而為什麼關系數據庫 模式中心 (6) 和那麼多其他數據庫相關的插件被激活了?插入的“可以被延遲 ”注釋表示 Eclipse 有能力以體系化的解決方案使它們在需要之前不被激活, 這只是關於屬性的一種特殊情況。但是,即使全面地考慮,這些大部分都是無足 輕重的。然而,還是有一些插件消耗了大量的啟動時間,而且,如我們所看到的 ,它們的開銷與處理擴展點期間插件激活緊密聯系在一起。下一節更全面地解釋 了這一開銷,並對啟動開銷是如何產生的進行了研究。然後我們將返回到我們對 J2EE 透視圖啟動的分析。

理解插件激活與擴展點處理之間的關系

通過避免對 subuilder 插件(按它的插件清單的說法,它是 Stored Procedure和 UDF Builder)的激活來節省啟動時間看起來可以成功,因為它需 要用去總時間的 10%。此外,它幾乎是在激活序列的最後(73 個中的第 72 個 ),所以,進行“修剪 (trim off)”可能要比避免次序靠前的插件激活更為簡 單。這個插件激活的棧記錄見圖 8。

圖 8. com.ibm.etools.subuilder 的插件激活棧記錄

Studio 的源代碼不需要去理解發生了什麼;棧記錄就足以指出原因。Eclipse 類 PartPane 管理您看到的那些選項卡視圖,包括 J2EE Hierarchy、 Package Explorer和 Navigator 視圖。如圖 8 所示,在選擇的文本下方,選項 卡視圖 PartPane 正在嘗試創建與 J2EE Hierarchy 選項卡相關聯的視圖的一個 實例,類的名字為 J2EEView 。進一步查看記錄的上方,我們看到這個類反過來 調用了一個幫助者 (helper),然後這個幫助者調用了 createExecutableExtension 方法。

IConfigurationElement.createExecutableExtension 方法值得特別注意。當您調試您自己的性能問題時,您將會發現,這個方法導致了 Rumtime Spy 沒 有發現的很多插件的激活案例。為更好地理解它做了些什麼以及它如何影響插件 激活,考慮清單 1 中給出的簡單的擴展點貢獻。您可能會發現,這與 第 1 部 分中規范的“Heloo Eclipse”樣例的清單是相同的。

清單 1. “Hello, Eclipse”的樣例擴展點

<?xml  version="1.0" encoding="UTF-8"?>
<plugin ...>


     ... lines omitted ...

   <runtime>


     <library name="hello.jar"/>
      <export name="*"/>
    </library>
   </runtime>
   <requires>
    <import plugin="org.eclipse.core.resources"/>


     <import plugin="org.eclipse.ui"/>
   </requires>


     ... lines omitted ...
   <extension point="org.eclipse.ui.actionSets">
    <actionSet
      label="Sample Action Set"
      visible="true"
      id="hello.actionSet">
      <menu
       label="Sample &Menu"
       id="sampleMenu">
       <separator
         name="sampleGroup">
       </separator>
      </menu>
      <action
       label="&Sample Action"
       icon="icons/sample.gif"


     class="hello.actions.SampleAction"
       tooltip="Hello, Eclipse world"
       menubarPath="sampleMenu/sampleGroup"
       toolbarPath="sampleGroup"  id="hello.actions.SampleAction">
      </action>
    </actionSet>
   </extension>

注意對 <action> 標簽的 class 屬性 的處理。這指定了將來處理菜單選項選擇的類的名字,這個類必須實現 IWorkbenchWindowActionDelegate 接口。這個類並不位於處理 org.eclipse.ui.actionSets 擴展點的同一個插件中(Workbench UI 插件), 也不位於它的先決插件中。那麼,當它看起來不取決於插件的 classpath 時, Workbench UI 插件如何去創建這個類的一個實例呢?

插件的類加載器 (classloader) 負責在它的庫中解析對類的引用。類加載器 將插件引用映射到它們相應的包含有插件類的 JAR 文件,比如我們的例子, SampleAction 。因此,Workbench UI 插件不必“知道”通過屬性引用的類,比 如 <action> 標簽的 class 。相反,是由於“Hello Eclipse”插件定義 了對 Workbench UI 插件的依賴而使之可以訪問目標插件的類,因而有權通過目 標插件的類加載器訪問 Hello Eclipse 的類。簡潔地重述一遍,當 Eclipse 找 不到某個類時,它會要求目標插件的類加載器去加載那個類;那就是 createExecutableExtension 方法所做的事情。

避免與擴展點處理相關的過早的插件激活

回到圖 8 中所示的棧記錄,我們可以發現,在所選擇的文本中部的代碼通過 調用 createExecutableExtension 對擴展貢獻進行處理 —— 看吧! 插件開始 加載了。我們現在要尋覓的是明擺著的問題的答案:這時需要加載它嗎?可以將 它的加載向後延遲嗎?

實際上所有會激活另一個插件的類最終都可以追溯到對 createExecutableExtension 方法的調用。這些調用的調用參數指明了屬性名( 通常是 “ class ”)和它的值,即要加載的類的全名。這就帶我們回到了導致 激活(此插件)的插件擴展貢獻,在我們的例子中也就是 subuilder 插件。清 單 2 是它的清單的摘錄。

清單 2. Subuilder plugin.xml 摘錄

<extension point =  "com.ibm.etools.rsc.sp">
   <sp 
    id = "com.ibm.etools.subuilder.sp.NewSQLSPAction"
    name="%STR_NEWWIZARD_SQLSP"
    group ="SPFolderAction.New"
    view = "datadef"
    class="com.ibm.etools.subuilder.actions.create.NewSQLSPAction"/>
  </extension>

就參數而言,這個擴展貢獻看起來類似於用來定義菜單選項貢獻的 org.eclipse.ui.popupmenu 擴展點;更確切地說,它定義了一個標簽(屬性 “name”),位置(屬性“group”),目標(屬性“view”),還有最特別的 ,處理程序(屬性 “class”)。Eclipse 擴展與這個特殊實現的區別是, Eclipse 的擴展創建一個委派類作為菜單選項的代理。直到菜單選項真正被選擇 時,相應的處理程序類才會被創建,因而延遲了包含此處理器類的插件的激活。將同樣的代理策略應用到前面的例子中,J2EE 透視圖的打開快了差不多 7 秒。創建 NewSQLSPAction 實例和激活包含它引用的類的插件的開銷並沒有消除,而 是改變為隨取隨付 (pay-as-you-go)。

結束語

現在您應該知道為什麼本文的子標題定為“成功的騙局”了。這完全是為了 滿足用戶的期望,並讓用戶不去理會您希望他們不要去注意的東西。在這個特例 中,現在啟動更快了,但是稍後如果用戶啟用數據庫工具,他們還要是付出同樣 多的 CPU 開銷。那是可以接受的,因為開銷是以化整為零的方式付出,而且, 更重要的是,它使延遲更接近用戶期望付出代價的相關動作。

記住,目標是 盡可能延遲代碼的執行,不僅因為它可以帶來感覺上性能的提 高,還因為最容易用於優化速度的代碼是永遠不執行的代碼。Eclipse 的插件體 系結構讓您可以容易地創建達成此目的的擴展,Runtime Spy 幫助您找到那些可 以對實現進行改進的情形。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved