原文引自:
http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx
第 8 章 — 智能客戶端應(yīng)用程序性能
發(fā)布日期: 08/20/2004 | 更新日期: 08/20/2004
本頁內(nèi)容
智能客戶端應(yīng)用程序可以提供比 Web 應(yīng)用程序更豐富和響應(yīng)速度更快的用戶界面,并且可以利用本地系統(tǒng)資源。如果應(yīng)用程序的大部分駐留在用戶的計算機上,則應(yīng)用程序不需要到 Web 服務(wù)器的持續(xù)的往返行程。這有利于提高性能和響應(yīng)性。然而,要實現(xiàn)智能客戶端應(yīng)用程序的全部潛能,您應(yīng)該在應(yīng)用程序的設(shè)計階段仔細考慮性能問題。通過在規(guī)劃和設(shè)計您的應(yīng)用程序時解決性能問題,可以幫助您及早控制成本,并減小以后陷入性能問題的可能性。
注改善智能客戶端應(yīng)用程序的性能并不僅限于應(yīng)用程序設(shè)計問題。您可以在整個應(yīng)用程序生存期中采取許多個步驟來使 .NET 代碼具有更高的性能。雖然 .NET 公共語言運行庫 (CLR) 在執(zhí)行代碼方面非常有效,但您可以使用多種技術(shù)來提高代碼的性能,并防止在代碼級引入性能問題。有關(guān)這些問題的詳細信息,請參閱http://msdn.microsoft.com/perf。
在應(yīng)用程序的設(shè)計中定義現(xiàn)實的性能要求并識別潛在的問題顯然是重要的,但是性能問題通常只在編寫代碼之后對其進行測試時出現(xiàn)。在這種情況下,您可以使用一些工具和技術(shù)來跟蹤性能問題。
本章分析如何設(shè)計和調(diào)整您的智能客戶端應(yīng)用程序以獲得最佳性能。它討論了許多設(shè)計和體系結(jié)構(gòu)問題(包括線程處理和緩存注意事項),并且分析了如何增強應(yīng)用程序的 Windows 窗體部分的性能。本章還介紹了您可以用來跟蹤和診斷智能客戶端應(yīng)用程序性能問題的一些技術(shù)和工具。
針對性能進行設(shè)計
您可以在應(yīng)用程序設(shè)計或體系結(jié)構(gòu)級完成許多工作,以確保智能客戶端應(yīng)用程序具有良好的性能。您應(yīng)該確保在設(shè)計階段盡可能早地制定現(xiàn)實的且可度量的性能目標,以便評估設(shè)計折衷,并且提供最劃算的方法來解決性能問題。只要可能,性能目標就應(yīng)該基于實際的用戶和業(yè)務(wù)要求,因為這些要求受到應(yīng)用程序所處的操作環(huán)境的強烈影響。性能建模是一種結(jié)構(gòu)化的且可重復(fù)的過程,您可以使用該過程來管理您的應(yīng)用程序并確保其實現(xiàn)性能目標。有關(guān)詳細信息,請參閱 Improving .NET Application Performance and Scalability 中的第 2 章“Performance Modeling”,網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt02.asp。
智能客戶端通常是較大的分布式應(yīng)用程序的組成部分。很重要的一點是在完整應(yīng)用程序的上下文中考慮智能客戶端應(yīng)用程序的性能,包括該客戶端應(yīng)用程序使用的所有位于網(wǎng)絡(luò)中的資源。微調(diào)并優(yōu)化應(yīng)用程序中的每一個組件通常是不必要或不可能的。相反,性能調(diào)整應(yīng)該基于優(yōu)先級、時間、預(yù)算約束和風(fēng)險。一味地追求高性能通常并不是一種劃算的策略。
智能客戶端還將需要與用戶計算機上的其他應(yīng)用程序共存。當您設(shè)計智能客戶端應(yīng)用程序時,您應(yīng)該考慮到您的應(yīng)用程序?qū)⑿枰c客戶端計算機上的其他應(yīng)用程序共享系統(tǒng)資源,例如,內(nèi)存、CPU 時間和網(wǎng)絡(luò)利用率。
注有關(guān)設(shè)計可伸縮的高性能遠程服務(wù)的信息,請參閱Improving .NET Performance and Scalability,網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenet.asp。本指南包含有關(guān)如何優(yōu)化 .NET 代碼以獲得最佳性能的詳細信息。
要設(shè)計高性能的智能客戶端,請考慮下列事項:
? |
在適當?shù)奈恢镁彺鏀?shù)據(jù)。數(shù)據(jù)緩存可以顯著改善智能客戶端應(yīng)用程序的性能,使您可以在本地使用數(shù)據(jù),而不必經(jīng)常從網(wǎng)絡(luò)檢索數(shù)據(jù)。但是,敏感數(shù)據(jù)或頻繁更改的數(shù)據(jù)通常不適合進行緩存。 |
? |
優(yōu)化網(wǎng)絡(luò)通訊。如果通過“健談的”接口與遠程層服務(wù)進行通訊,并且借助于多個請求/響應(yīng)往返行程來執(zhí)行單個邏輯操作,則可能消耗系統(tǒng)和網(wǎng)絡(luò)資源,從而導(dǎo)致低劣的應(yīng)用程序性能。 |
? |
有效地使用線程。如果您使用用戶界面 (UI) 線程執(zhí)行阻塞 I/O 綁定調(diào)用,則 UI 似乎不對用戶作出響應(yīng)。因為創(chuàng)建和關(guān)閉線程需要系統(tǒng)開銷,所以創(chuàng)建大量不必要的線程可能導(dǎo)致低劣的性能。 |
? |
有效地使用事務(wù)。如果客戶端具有本地數(shù)據(jù),則使用原子事務(wù)可幫助您確保該數(shù)據(jù)是一致的。因為數(shù)據(jù)是本地的,所以事務(wù)也是本地的而不是分布式的。對于脫機工作的智能客戶端而言,對本地數(shù)據(jù)進行的任何更改都是暫時的。客戶端在重新聯(lián)機時需要同步更改。對于非本地數(shù)據(jù)而言,在某些情況下可以使用分布式事務(wù)(例如,當服務(wù)位于具有良好連接性的同一物理位置并且服務(wù)支持它時)。諸如 Web 服務(wù)和消息隊列之類的服務(wù)不支持分布式事務(wù)。 |
? |
優(yōu)化應(yīng)用程序啟動時間。較短的應(yīng)用程序啟動時間使用戶可以更為迅速地開始與應(yīng)用程序交互,從而使用戶立刻對應(yīng)用程序的性能和可用性產(chǎn)生好感。應(yīng)該對您的應(yīng)用程序進行適當?shù)脑O(shè)計,以便在應(yīng)用程序啟動時僅加載那些必需的程序集。因為加載每個程序集都會引起性能開銷,所以請避免使用大量程序集。 |
? |
有效地管理可用資源。低劣的設(shè)計決策(例如,實現(xiàn)不必要的完成器,未能在 Dispose 方法中取消終止,或者未能釋放非托管資源)可能導(dǎo)致在回收資源時發(fā)生不必要的延遲,并且可能造成使應(yīng)用程序性能降低的資源泄漏。如果應(yīng)用程序未能正確地釋放資源,或者應(yīng)用程序顯式強制進行垃圾回收,則可能會妨礙 CLR 有效地管理內(nèi)存。 |
? |
優(yōu)化 Windows 窗體性能。智能客戶端應(yīng)用程序依靠 Windows 窗體來提供內(nèi)容豐富且響應(yīng)迅速的用戶界面.您可以使用多種技術(shù)來確保 Windows 窗體提供最佳性能。這些技術(shù)包括降低用戶界面的復(fù)雜性,以及避免同時加載大量數(shù)據(jù)。 |
在許多情況下,從用戶角度感受到的應(yīng)用程序性能起碼與應(yīng)用程序的實際性能同樣重要。您可以通過對設(shè)計進行某些特定的更改來創(chuàng)建在用戶看來性能高得多的應(yīng)用程序,例如:使用后臺異步處理(以使 UI 能作出響應(yīng));顯示進度欄以指示任務(wù)的進度;提供相應(yīng)的選項以便用戶取消長期運行的任務(wù)。
本節(jié)將專門詳細討論這些問題。
數(shù)據(jù)緩存原則
緩存是一種能夠改善應(yīng)用程序性能并提供響應(yīng)迅速的用戶界面的重要技術(shù)。您應(yīng)該考慮下列選項:
? |
緩存頻繁檢索的數(shù)據(jù)以減少往返行程。如果您的應(yīng)用程序必須頻繁地與網(wǎng)絡(luò)服務(wù)交互以檢索數(shù)據(jù),則應(yīng)該考慮在客戶端緩存數(shù)據(jù),從而減少通過網(wǎng)絡(luò)重復(fù)獲取數(shù)據(jù)的需要。這可以極大地提高性能,提供對數(shù)據(jù)的近乎即時的訪問,并且消除了可能對智能客戶端應(yīng)用程序性能造成不利影響的網(wǎng)絡(luò)延遲和中斷風(fēng)險。 |
? |
緩存只讀引用數(shù)據(jù)。只讀引用數(shù)據(jù)通常是理想的緩存對象。此類數(shù)據(jù)用于提供進行驗證和用戶界面顯示所需的數(shù)據(jù),例如,產(chǎn)品說明、ID 等等。因為客戶端無法更改此類數(shù)據(jù),所以通常可以在客戶端緩存它而無須進行任何進一步的特殊處理。 |
? |
緩存要發(fā)送給位于網(wǎng)絡(luò)上的服務(wù)的數(shù)據(jù)。您應(yīng)該考慮緩存要發(fā)送給位于網(wǎng)絡(luò)上的服務(wù)的數(shù)據(jù)。例如,如果您的應(yīng)用程序允許用戶輸入由在多個窗體中收集的一些離散數(shù)據(jù)項組成的定單信息,則請考慮允許用戶輸入全部數(shù)據(jù),然后在輸入過程的結(jié)尾在一個網(wǎng)絡(luò)調(diào)用中發(fā)送定單信息。 |
? |
盡量少地緩存高度不穩(wěn)定的數(shù)據(jù)。在緩存任何不穩(wěn)定的數(shù)據(jù)之前,您需要考慮在其變得陳舊或者由于其他原因變得不可用之前,能夠?qū)⑵渚彺娑嚅L時間。如果數(shù)據(jù)高度不穩(wěn)定并且您的應(yīng)用程序依賴于最新信息,則或許只能將數(shù)據(jù)緩存很短一段時間(如果可以緩存)。 |
? |
盡量少地緩存敏感數(shù)據(jù)。您應(yīng)該避免在客戶端上緩存敏感數(shù)據(jù),因為在大多數(shù)情況下,您無法保證客戶端的物理安全。但是,如果您必須在客戶端上緩存敏感數(shù)據(jù),則您通常將需要加密數(shù)據(jù),該操作本身也會影響性能。 |
有關(guān)數(shù)據(jù)緩存的其他問題的詳細信息,請參閱本指南的第 2 章。另請參閱 Improving .NET Application Performance and Scalability 的第 3 章“Design Guidelines for Application Performance”(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt03.asp) 的“Caching”一節(jié)以及 Improving .NET Application Performance and Scalability 的第 4 章“Architecture and Design Review of .NET Application for Performance and Scalability”(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt04.asp)。
網(wǎng)絡(luò)通訊原則
您將面臨的另一個決策是如何設(shè)計和使用網(wǎng)絡(luò)服務(wù),例如,Web 服務(wù)。特別地,您應(yīng)該考慮與網(wǎng)絡(luò)服務(wù)交互的粒度、同步性和頻率。要獲得最佳的性能和可伸縮性,您應(yīng)該在單個調(diào)用中發(fā)送更多的數(shù)據(jù),而不是在多個調(diào)用中發(fā)送較少量的數(shù)據(jù)。例如,如果您的應(yīng)用程序允許用戶在定單中輸入多個項,則較好的做法是為所有項收集數(shù)據(jù),然后將完成的采購定單一次性發(fā)送給服務(wù),而不是在多個調(diào)用中發(fā)送單個項的詳細信息。除了降低與進行大量網(wǎng)絡(luò)調(diào)用相關(guān)聯(lián)的系統(tǒng)開銷以外,這還可以減少服務(wù)和/或客戶端內(nèi)的復(fù)雜狀態(tài)管理的需要。
應(yīng)該將您的智能客戶端應(yīng)用程序設(shè)計為盡可能地使用異步通訊,因為這將有助于使用戶界面快速響應(yīng)以及并行執(zhí)行任務(wù)。有關(guān)如何使用 BeginInvoke 和 EndInvoke 方法異步啟動調(diào)用和檢索數(shù)據(jù)的詳細信息,請參閱“Asynchronous Programming Overview”(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpovrasynchronousprogrammingoverview.asp)。
注有關(guān)設(shè)計和構(gòu)建偶爾連接到網(wǎng)絡(luò)的智能客戶端應(yīng)用程序的詳細信息,請參閱第 3 章“建立連接”和第 4 章“偶爾連接的智能客戶端”。
線程處理原則
在應(yīng)用程序內(nèi)使用多個線程可能是一種提高其響應(yīng)性和性能的好方法。特別地,您應(yīng)該考慮使用線程來執(zhí)行可以在后臺安全地完成且不需要用戶交互的處理。通過在后臺執(zhí)行此類工作,可以使用戶能夠繼續(xù)使用應(yīng)用程序,并且使應(yīng)用程序的主用戶界面線程能夠維持應(yīng)用程序的響應(yīng)性。
適合于在單獨的線程上完成的處理包括:
? |
應(yīng)用程序初始化。請在后臺線程上執(zhí)行漫長的初始化,以便用戶能夠盡快地與您的應(yīng)用程序交互,尤其是在應(yīng)用程序功能的重要或主要部分并不依賴于該初始化完成時。 |
? |
遠程服務(wù)調(diào)用。請在單獨的后臺線程上通過網(wǎng)絡(luò)進行所有遠程調(diào)用。很難(如果不是無法)保證位于網(wǎng)絡(luò)上的服務(wù)的響應(yīng)時間。在單獨的線程上執(zhí)行這些調(diào)用可以減少發(fā)生網(wǎng)絡(luò)中斷或延遲的風(fēng)險,從而避免對應(yīng)用程序性能造成不利影響。 |
? |
IO 綁定處理。應(yīng)該在單獨的線程上完成諸如在磁盤上搜索和排序數(shù)據(jù)之類的處理。通常,這種工作要受到磁盤 I/O 子系統(tǒng)而不是處理器可用性的限制,因此當該工作在后臺執(zhí)行時,您的應(yīng)用程序可以有效地維持其響應(yīng)性。 |
盡管使用多個線程的性能好處可能很顯著,但需要注意,線程使用它們自己的資源,并且使用太多的線程可能給處理器(它需要管理線程之間的上下文切換)造成負擔(dān)。要避免這一點,請考慮使用線程池,而不是創(chuàng)建和管理您自己的線程。線程池將為您有效地管理線程,重新使用現(xiàn)有的線程對象,并且盡可能地減小與線程創(chuàng)建和處置相關(guān)聯(lián)的系統(tǒng)開銷。
如果用戶體驗受到后臺線程所執(zhí)行的工作的影響,則您應(yīng)該總是讓用戶了解工作的進度。以這種方式提供反饋可以增強用戶對您的應(yīng)用程序的性能的感覺,并且防止他或她假設(shè)沒有任何事情發(fā)生。請努力確保用戶可以隨時取消漫長的操作。
您還應(yīng)該考慮使用 Application 對象的 Idle 事件來執(zhí)行簡單的操作。Idle 事件提供了使用單獨的線程來進行后臺處理的簡單替代方案。當應(yīng)用程序不再有其他用戶界面消息需要處理并且將要進入空閑狀態(tài)時,該事件將激發(fā)。您可以通過該事件執(zhí)行簡單的操作,并且利用用戶不活動的情況。例如:
[C#]
public Form1()
{
InitializeComponent();
Application.Idle += new EventHandler( OnApplicationIdle );
}
private void OnApplicationIdle( object sender, EventArgs e )
{
}
[Visual Basic .NET]
Public Class Form1
Inherits System.Windows.Forms.Form
Public Sub New()
MyBase.New()
InitializeComponent()
AddHandler Application.Idle, AddressOf OnApplicationIdle
End Sub
Private Sub OnApplicationIdle(ByVal sender As System.Object, ByVal e As System.EventArgs)
End Sub
End Class
注有關(guān)在智能客戶端中使用多個線程的詳細信息,請參閱第 6 章“使用多個線程”。
事務(wù)原則
事務(wù)可以提供重要的支持,以確保不會違反業(yè)務(wù)規(guī)則并維護數(shù)據(jù)一致性。事務(wù)可以確保一組相關(guān)任務(wù)作為一個單元成功或失敗。您可以使用事務(wù)來維護本地數(shù)據(jù)庫和其他資源(包括消息隊列的隊列)之間的一致性。
對于需要在網(wǎng)絡(luò)連接不可用時使用脫機緩存數(shù)據(jù)的智能客戶端應(yīng)用程序,您應(yīng)該將事務(wù)性數(shù)據(jù)排隊,并且在網(wǎng)絡(luò)連接可用時將其與服務(wù)器進行同步。
您應(yīng)該避免使用涉及到位于網(wǎng)絡(luò)上的資源的分布式事務(wù),因為這些情況可能導(dǎo)致與不斷變化的網(wǎng)絡(luò)和資源響應(yīng)時間有關(guān)的性能問題。如果您的應(yīng)用程序需要在事務(wù)中涉及到位于網(wǎng)絡(luò)上的資源,則應(yīng)該考慮使用補償事務(wù),以便使您的應(yīng)用程序能夠在本地事務(wù)失敗時取消以前的請求。盡管補償事務(wù)在某些情況下可能不適用,但它們使您的應(yīng)用程序能夠按照松耦合方式在事務(wù)的上下文內(nèi)與網(wǎng)絡(luò)資源交互,從而減少了不在本地計算機控制之下的資源對應(yīng)用程序的性能造成不利影響的可能性。
注有關(guān)在智能客戶端中使用事務(wù)的詳細信息,請參閱第 3 章“建立連接”。
優(yōu)化應(yīng)用程序啟動時間
快速的應(yīng)用程序啟動時間幾乎可以使用戶立即開始與應(yīng)用程序交互,從而使用戶立刻對應(yīng)用程序的性能和可用性產(chǎn)生好感。
當應(yīng)用程序啟動時,首先加載 CLR,再加載應(yīng)用程序的主程序集,隨后加載為解析從應(yīng)用程序的主窗體中引用的對象的類型所需要的所有程序集。CLR 在該階段不會 加載所有相關(guān)程序集;它僅加載包含主窗體類上的成員變量的類型定義的程序集。在加載了這些程序集之后,實時 (JIT) 編譯器將在方法運行時編譯方法的代碼(從 Main 方法開始)。同樣,JIT 編譯器不會 編譯您的程序集中的所有代碼。相反,將根據(jù)需要逐個方法地編譯代碼。
要盡可能減少應(yīng)用程序的啟動時間,您應(yīng)該遵循下列原則:
? |
盡可能減少應(yīng)用程序主窗體類中的成員變量。這將在 CLR 加載主窗體類時盡可能減少必須解析的類型數(shù)量。 |
? |
盡量不要立即使用大型基類程序集(XML 庫或 ADO.NET 庫)中的類型。這些程序集的加載很費時間。使用應(yīng)用程序配置類和跟蹤開關(guān)功能時將引入 XML 庫。如果要優(yōu)先考慮應(yīng)用程序啟動時間,請避免這一點。 |
? |
盡可能使用惰性加載。僅在需要時獲取數(shù)據(jù),而不是提前加載和凍結(jié) UI。 |
? |
將應(yīng)用程序設(shè)計為使用較少的程序集。帶有大量程序集的應(yīng)用程序會招致性能開銷增加。這些開銷來自加載元數(shù)據(jù)、訪問 CLR 中的預(yù)編譯映像中的各種內(nèi)存頁以加載程序集(如果它是用本機映像生成器工具 Ngen.exe 預(yù)編譯的)、JIT 編譯時間、安全檢查等等。您應(yīng)該考慮基于程序集的使用模式來合并程序集,以便降低相關(guān)聯(lián)的性能開銷。 |
? |
避免設(shè)計將多個組件的功能組合到一個組件中的單一類。將設(shè)計分解到多個只須在實際調(diào)用時進行編譯的較小類。 |
? |
將應(yīng)用程序設(shè)計為在初始化期間對網(wǎng)絡(luò)服務(wù)進行并行調(diào)用。通過在初始化期間調(diào)用可以并行運行的網(wǎng)絡(luò)服務(wù),可以利用服務(wù)代理提供的異步功能。這有助于釋放當前執(zhí)行的線程并且并發(fā)地調(diào)用服務(wù)以完成任務(wù)。 |
? |
使用 NGEN.exe 編譯和試驗 NGen 和非 NGen 程序集,并且確定哪個程序集保存了最大數(shù)量的工作集頁面。NGEN.exe(它隨附在 .NET Framework 中)用于預(yù)編譯程序集以創(chuàng)建本機映像,該映像隨后被存儲在全局程序集緩存的特殊部分,以便應(yīng)用程序下次需要它時使用。通過創(chuàng)建程序集的本機映像,可以使程序集更快地加載和執(zhí)行,因為 CLR 不需要動態(tài)生成程序集中包含的代碼和數(shù)據(jù)結(jié)構(gòu)。有關(guān)詳細信息,請參閱 Improving .NET Application Performance and Scalability 的第 5 章“Improving Managed Code Performance”中的“Working Set Considerations”和“NGen.exe Explained”部分,網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp。
注如果您使用 NGEN 預(yù)編譯程序集,則會立即加載它的所有依賴程序集。 |
管理可用資源
公共語言運行庫 (CLR) 使用垃圾回收器來管理對象生存期和內(nèi)存使用。這意味著無法再訪問的對象將被垃圾回收器自動回收,并且自動回收內(nèi)存。由于多種原因無法再訪問對象。例如,可能沒有對該對象的任何引用,或者對該對象的所有引用可能來自其他可作為當前回收周期的一部分進行回收的對象。盡管自動垃圾回收使您的代碼不必負責(zé)管理對象刪除,但這意味著您的代碼不再對對象的確切刪除時間具有顯式控制。
請考慮下列原則,以確保您能夠有效地管理可用資源:
? |
確保在被調(diào)用方對象提供 Dispose 方法時該方法得到調(diào)用。如果您的代碼調(diào)用了支持 Dispose 方法的對象,則您應(yīng)該確保在使用完該對象之后立即調(diào)用此方法。調(diào)用 Dispose 方法可以確保搶先釋放非托管資源,而不是等到發(fā)生垃圾回收。除了提供 Dispose 方法以外,某些對象還提供其他管理資源的方法,例如,Close 方法。在這些情況下,您應(yīng)該參考文檔資料以了解如何使用其他方法。例如,對于 SqlConnection 對象而言,調(diào)用 Close 或 Dispose 都足可以搶先將數(shù)據(jù)庫連接釋放回連接池中。一種可以確保您在對象使用完畢之后立即調(diào)用 Dispose 的方法是使用 Visual C# .NET 中的 using 語句或 Visual Basic .NET 中的 Try/Finally 塊。
下面的代碼片段演示了 Dispose 的用法。
C# 中的 using 語句示例: using( StreamReader myFile = new StreamReader("C:\\ReadMe.Txt")){
string contents = myFile.ReadToEnd();
//... use the contents of the file
} // dispose is called and the StreamReader's resources released
Visual Basic .NET 中的 Try/Finally 塊示例: Dim myFile As StreamReader
myFile = New StreamReader("C:\\ReadMe.Txt")
Try
String contents = myFile.ReadToEnd()
'... use the contents of the file
Finally
myFile.Close()
End Try
注在 C# 和 C++ 中,F(xiàn)inalize 方法是作為析構(gòu)函數(shù)實現(xiàn)的。在 Visual Basic .NET 中,Finalize 方法是作為 Object 基類上的 Finalize 子例程的重寫實現(xiàn)的。 |
? |
如果您在客戶端調(diào)用過程中占據(jù)非托管資源,則請?zhí)峁?/B> Finalize 和 Dispose 方法。如果您在公共或受保護的方法調(diào)用中創(chuàng)建訪問非托管資源的對象,則應(yīng)用程序需要控制非托管資源的生存期。在圖 8.1 中,第一種情況是對非托管資源的調(diào)用,在此將打開、獲取和關(guān)閉資源。在此情況下,您的對象無須提供 Finalize 和 Dispose 方法。在第二種情況下,在方法調(diào)用過程中占據(jù)非托管資源;因此,您的對象應(yīng)該提供 Finalize 和 Dispose 方法,以便客戶端在使用完該對象后可以立即顯式釋放資源。
圖 8.1:Dispose 和 Finalize 方法調(diào)用的用法
|
垃圾回收通常有利于提高總體性能,因為它將速度的重要性置于內(nèi)存利用率之上。只有當內(nèi)存資源不足時,才需要刪除對象;否則,將使用所有可用的應(yīng)用程序資源以使您的應(yīng)用程序受益。但是,如果您的對象保持對非托管資源(例如,窗口句柄、文件、GDI 對象和網(wǎng)絡(luò)連接)的引用,則程序員通過在這些資源不再使用時顯式釋放它們可以獲得更好的性能。如果您要在客戶端方法調(diào)用過程中占據(jù)非托管資源,則對象應(yīng)該允許調(diào)用方使用 IDisposable 接口(它提供 Dispose 方法)顯式管理資源。通過實現(xiàn) IDisposable,對象將通知它可被要求明確進行清理,而不是等待垃圾回收。實現(xiàn) IDisposable 的對象的調(diào)用方在使用完該對象后將簡單地調(diào)用 Dispose 方法,以便它可以根據(jù)需要釋放資源。
有關(guān)如何在某個對象上實現(xiàn) IDisposable 的詳細信息,請參閱 Improving .NET Application Performance and Scalability 中的第 5 章“Improving Managed Code Performance”,網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp。
注如果您的可處置對象派生自另一個也實現(xiàn)了 IDisposable 接口的對象,則您應(yīng)該調(diào)用基類的 Dispose 方法以使其可以清理它的資源。您還應(yīng)該調(diào)用實現(xiàn)了 IDisposable 接口的對象所擁有的所有對象上的 Dispose。
Finalize 方法也使您的對象可以在刪除時顯式釋放其引用的任何資源。由于垃圾回收器所具有的非確定性,在某些情況下,Finalize 方法可能長時間不會被調(diào)用。實際上,如果您的應(yīng)用程序在垃圾回收器刪除對象之前終止,則該方法可能永遠不會被調(diào)用。然而,需要使用 Finalize 方法作為一種后備策略,以防調(diào)用方?jīng)]有顯式調(diào)用 Dispose 方法(Dispose 和 Finalize 方法共享相同的資源清理代碼)。通過這種方式,可能在某個時刻釋放資源,即使這發(fā)生在最佳時刻之后。
注要確保 Dispose 和 Finalize 中的清理代碼不會被調(diào)用兩次,您應(yīng)該調(diào)用 GC.SuppressFinalize 以通知垃圾回收器不要調(diào)用 Finalize 方法。
垃圾回收器實現(xiàn)了 Collect 方法,該方法強制垃圾回收器刪除所有對象掛起刪除。不應(yīng)該從應(yīng)用程序內(nèi)調(diào)用該方法,因為回收周期在高優(yōu)先級線程上運行。回收周期可能凍結(jié)所有 UI 線程,從而使得用戶界面停止響應(yīng)。
有關(guān)詳細信息,請參閱 Improving .NET Application Performance and Scalability 中的“Garbage Collection Guidelines”、“Finalize and Dispose Guidelines”、“Dispose Pattern”和“Finalize and Dispose Guidelines”,網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp。
優(yōu)化 Windows 窗體性能
Windows 窗體為智能客戶端應(yīng)用程序提供了內(nèi)容豐富的用戶界面,并且您可以使用許多種技術(shù)來幫助確保 Windows 窗體提供最佳性能。在討論特定技術(shù)之前,對一些可以顯著提高 Windows 窗體性能的高級原則進行回顧是有用的。
? |
小心創(chuàng)建句柄。Windows 窗體將句柄創(chuàng)建虛擬化(即,它動態(tài)創(chuàng)建和重新創(chuàng)建窗口句柄對象)。創(chuàng)建句柄對象的系統(tǒng)開銷可能非常大;因此,請避免進行不必要的邊框樣式更改或者更改 MDI 父對象。 |
? |
避免創(chuàng)建帶有太多子控件的應(yīng)用程序。Microsoft? Windows? 操作系統(tǒng)限制每個進程最多有 10,000 個控件,但您應(yīng)該避免在窗體上使用成百上千個控件,因為每個控件都要消耗內(nèi)存資源。 |
本節(jié)的其余部分討論您可以用來優(yōu)化應(yīng)用程序用戶界面性能的更為具體的技術(shù)。
使用 BeginUpdate 和 EndUpdate
許多 Windows 窗體控件(例如,ListView 和 TreeView 控件)實現(xiàn)了 BeginUpdate 和 EndUpdate 方法,它們在操縱基礎(chǔ)數(shù)據(jù)或控件屬性時取消了控件的重新繪制。通過使用 BeginUpdate 和 EndUpdate 方法,您可以對控件進行重大更改,并且避免在應(yīng)用這些更改時讓控件經(jīng)常重新繪制自身。此類重新繪制會導(dǎo)致性能顯著降低,并且用戶界面閃爍且不反應(yīng)。
例如,如果您的應(yīng)用程序具有一個要求添加大量節(jié)點項的樹控件,則您應(yīng)該調(diào)用 BeginUpdate,添加所有必需的節(jié)點項,然后調(diào)用 EndUpdate。下面的代碼示例顯示了一個樹控件,該控件用于顯示許多個客戶的層次結(jié)構(gòu)表示形式及其定單信息。
[C#]
// Suppress repainting the TreeView until all the objects have been created.
TreeView1.BeginUpdate();
// Clear the TreeView.
TreeView1.Nodes.Clear();
// Add a root TreeNode for each Customer object in the ArrayList.
foreach( Customer customer2 in customerArray )
{
TreeView1.Nodes.Add( new TreeNode( customer2.CustomerName ) );
// Add a child TreeNode for each Order object in the current Customer.
foreach( Order order1 in customer2.CustomerOrders )
{
TreeView1.Nodes[ customerArray.IndexOf(customer2) ].Nodes.Add(
new TreeNode( customer2.CustomerName + "." + order1.OrderID ) );
}
}
// Begin repainting the TreeView.
TreeView1.EndUpdate();
[Visual Basic .NET]
' Suppress repainting the TreeView until all the objects have been created.
TreeView1.BeginUpdate()
' Clear the TreeView
TreeView1.Nodes.Clear()
' Add a root TreeNode for each Customer object in the ArrayList
For Each customer2 As Customer In customerArray
TreeView1.Nodes.Add(New TreeNode(customer2.CustomerName))
' Add a child TreeNode for each Order object in the current Customer.
For Each order1 As Order In customer2.CustomerOrders
TreeView1.Nodes(Array.IndexOf(customerArray, customer2)).Nodes.Add( _
New TreeNode(customer2.CustomerName & "." & order1.OrderID))
Next
Next
' Begin repainting the TreeView.
TreeView1.EndUpdate()
即使在您不希望向控件添加許多對象時,您也應(yīng)該使用 BeginUpdate 和 EndUpdate 方法。在大多數(shù)情況下,您在運行之前將不知道要添加的項的確切個數(shù)。因此,為了妥善處理大量數(shù)據(jù)以及應(yīng)付將來的要求,您應(yīng)該總是調(diào)用 BeginUpdate 和 EndUpdate 方法。
注調(diào)用 Windows 窗體控件使用的許多 Collection 類的 AddRange 方法時,將自動為您調(diào)用 BeginUpdate 和 EndUpdate 方法。
使用 SuspendLayout 和 ResumeLayout
許多 Windows 窗體控件(例如,ListView 和 TreeView 控件)都實現(xiàn)了 SuspendLayout 和 ResumeLayout 方法,它們能夠防止控件在添加子控件時創(chuàng)建多個布局事件。
如果您的控件以編程方式添加和刪除子控件或者執(zhí)行動態(tài)布局,則您應(yīng)該調(diào)用 SuspendLayout 和 ResumeLayout 方法。通過 SuspendLayout 方法,可以在控件上執(zhí)行多個操作,而不必為每個更改執(zhí)行布局。例如,如果您調(diào)整控件的大小并移動控件,則每個操作都將引發(fā)單獨的布局事件。
這些方法按照與 BeginUpdate 和 EndUpdate 方法類似的方式操作,并且在性能和用戶界面穩(wěn)定性方面提供相同的好處。
下面的示例以編程方式向父窗體中添加按鈕:
[C#]
private void AddButtons()
{
// Suspend the form layout and add two buttons.
this.SuspendLayout();
Button buttonOK = new Button();
buttonOK.Location = new Point(10, 10);
buttonOK.Size = new Size(75, 25);
buttonOK.Text = "OK";
Button buttonCancel = new Button();
buttonCancel.Location = new Point(90, 10);
buttonCancel.Size = new Size(75, 25);
buttonCancel.Text = "Cancel";
this.Controls.AddRange(new Control[]{buttonOK, buttonCancel});
this.ResumeLayout();
}
[Visual Basic .NET]
Private Sub AddButtons()
' Suspend the form layout and add two buttons
Me.SuspendLayout()
Dim buttonOK As New Button
buttonOK.Location = New Point(10, 10)
buttonOK.Size = New Size(75, 25)
buttonOK.Text = "OK"
Dim buttonCancel As New Button
buttonCancel.Location = New Point(90, 10)
buttonCancel.Size = New Size(75, 25)
buttonCancel.Text = "Cancel"
Me.Controls.AddRange(New Control() { buttonOK, buttonCancel } )
Me.ResumeLayout()
End Sub
每當您添加或刪除控件、執(zhí)行子控件的自動布局或者設(shè)置任何影響控件布局的屬性(例如,大小、位置、定位點或停靠屬性)時,您都應(yīng)該使用 SuspendLayout 和 ResumeLayout 方法。
處理圖像
如果您的應(yīng)用程序顯示大量圖像文件(例如,.jpg 和 .gif 文件),則您可以通過以位圖格式預(yù)先呈現(xiàn)圖像來顯著改善顯示性能。
要使用該技術(shù),請首先從文件中加載圖像,然后使用 PARGB 格式將其呈現(xiàn)為位圖。下面的代碼示例從磁盤中加載文件,然后使用該類將圖像呈現(xiàn)為預(yù)乘的、Alpha 混合 RGB 格式。例如:
[C#]
if ( image != null && image is Bitmap )
{
Bitmap bm = (Bitmap)image;
Bitmap newImage = new Bitmap( bm.Width, bm.Height,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb );
using ( Graphics g = Graphics.FromImage( newImage ) )
{
g.DrawImage( bm, new Rectangle( 0,0, bm.Width, bm.Height ) );
}
image = newImage;
}
[Visual Basic .NET]
If Not(image Is Nothing) AndAlso (TypeOf image Is Bitmap) Then
Dim bm As Bitmap = CType(image, Bitmap)
Dim newImage As New Bitmap(bm.Width, bm.Height, _
System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
Using g As Graphics = Graphics.FromImage(newImage)
g.DrawImage(bm, New Rectangle(0, 0, bm.Width, bm.Height))
End Using
image = newImage
End If
使用分頁和惰性加載
在大多數(shù)情況下,您應(yīng)該僅在需要時檢索或顯示數(shù)據(jù)。如果您的應(yīng)用程序需要檢索和顯示大量信息,則您應(yīng)該考慮將數(shù)據(jù)分解到多個頁面中,并且一次顯示一頁數(shù)據(jù)。這可以使用戶界面具有更高的性能,因為它無須顯示大量數(shù)據(jù)。此外,這可以提高應(yīng)用程序的可用性,因為用戶不會同時面對大量數(shù)據(jù),并且可以更加容易地導(dǎo)航以查找他或她需要的確切數(shù)據(jù)。
例如,如果您的應(yīng)用程序顯示來自大型產(chǎn)品目錄的產(chǎn)品數(shù)據(jù),則您可以按照字母順序顯示這些項,并且將所有以“A”開頭的產(chǎn)品顯示在一個頁面上,將所有以“B”開頭的產(chǎn)品顯示在下一個頁面上。然后,您可以讓用戶直接導(dǎo)航到適當?shù)捻撁妫员闼蛩裏o須瀏覽所有頁面就可以獲得他或她需要的數(shù)據(jù)。
以這種方式將數(shù)據(jù)分頁還使您可以根據(jù)需要獲取后臺的數(shù)據(jù)。例如,您可能只需要獲取第一頁信息以便顯示并且讓用戶與其進行交互。然后,您可以獲取后臺中的、已經(jīng)準備好供用戶使用的下一頁數(shù)據(jù)。該技術(shù)在與數(shù)據(jù)緩存技術(shù)結(jié)合使用時可能特別有效。
您還可以通過使用惰性加載技術(shù)來提高智能客戶端應(yīng)用程序的性能。您無須立即加載可能在將來某個時刻需要的數(shù)據(jù)或資源,而是可以根據(jù)需要加載它們。您可以在構(gòu)建大型列表或樹結(jié)構(gòu)時使用惰性加載來提高用戶界面的性能。在此情況下,您可以在用戶需要看到數(shù)據(jù)時(例如,在用戶展開樹節(jié)點時)加載它。
優(yōu)化顯示速度
根據(jù)您用于顯示用戶界面控件和應(yīng)用程序窗體的技術(shù),您可以用多種不同的方式來優(yōu)化應(yīng)用程序的顯示速度。
當您的應(yīng)用程序啟動時,您應(yīng)該考慮盡可能地顯示簡單的用戶界面。這將減少啟動時間,并且向用戶呈現(xiàn)整潔且易于使用的用戶界面。而且,您應(yīng)該努力避免引用類以及在啟動時加載任何不會立刻需要的數(shù)據(jù)。這將減少應(yīng)用程序和 .NET Framework 初始化時間,并且提高應(yīng)用程序的顯示速度。
當您需要顯示對話框或窗體時,您應(yīng)該在它們做好顯示準備之前使其保持隱藏狀態(tài),以便減少需要的繪制工作量。這將有助于確保窗體僅在初始化之后顯示。
如果您的應(yīng)用程序具有的控件含有覆蓋整個客戶端表面區(qū)域的子控件,則您應(yīng)該考慮將控件背景樣式設(shè)置為不透明。這可以避免在發(fā)生每個繪制事件時重繪控件的背景。您可以通過使用 SetStyle 方法來設(shè)置控件的樣式。使用 ControlsStyles.Opaque 枚舉可以指定不透明控件樣式。
您應(yīng)該避免任何不必要的控件重新繪制操作。一種方法是在設(shè)置控件的屬性時隱藏控件。在 OnPaint 事件中具有復(fù)雜繪圖代碼的應(yīng)用程序能夠只重繪窗體的無效區(qū)域,而不是繪制整個窗體。OnPaint 事件的 PaintEventArgs 參數(shù)包含一個 ClipRect 結(jié)構(gòu),它指示窗口的哪個部分無效。這可以減少用戶等待查看完整顯示的時間。
使用標準的繪圖優(yōu)化,例如,剪輯、雙緩沖和 ClipRectangle。這還將通過防止對不可見或要求重繪的顯示部分執(zhí)行不必要的繪制操作,從而有助于改善智能客戶端應(yīng)用程序的顯示性能。有關(guān)增強繪圖性能的詳細信息,請參閱 Painting techniques using Windows Forms for the Microsoft .NET Framework,網(wǎng)址為:http://windowsforms.net/articles/windowsformspainting.aspx。
如果您的顯示包含動畫或者經(jīng)常更改某個顯示元素,則您應(yīng)該使用雙緩沖或多緩沖,在繪制當前圖像的過程中準備下一個圖像。System.Windows.Forms 命名空間中的 ControlStyles 枚舉適用于許多控件,并且 DoubleBuffer 成員可以幫助防止閃爍。啟用 DoubleBuffer 樣式將使您的控件繪制在離屏緩沖中完成,然后同時繪制到屏幕上。盡管這有助于防止閃爍,但它的確為分配的緩沖區(qū)使用了更多內(nèi)存。
性能調(diào)整和診斷
在設(shè)計和實現(xiàn)階段處理性能問題是實現(xiàn)應(yīng)用程序性能目標的最劃算的方法。但是,您只有在開發(fā)階段經(jīng)常且盡早測試應(yīng)用程序的性能,才能真正有效地優(yōu)化應(yīng)用程序的性能。
盡管針對性能進行設(shè)計和測試都很重要,但在這些早期階段優(yōu)化每個組件和所有代碼不是有效的資源用法,因此應(yīng)該予以避免。所以,應(yīng)用程序可能存在您在設(shè)計階段未預(yù)料到的性能問題。例如,您可能遇到由于兩個系統(tǒng)或組件之間的無法預(yù)料的交互而產(chǎn)生的性能問題,或者您可能使用原來存在的、未按希望的方式執(zhí)行的代碼。在此情況下,您需要追究性能問題的根源,以便您可以適當?shù)亟鉀Q該問題。
本節(jié)討論一些將幫助您診斷性能問題以及調(diào)整應(yīng)用程序以獲得最佳性能的工具和技術(shù)。
制定性能目標
當您設(shè)計和規(guī)劃智能客戶端應(yīng)用程序時,您應(yīng)該仔細考慮性能方面的要求,并且定義合適的性能目標。在定義這些目標時,請考慮您將如何度量應(yīng)用程序的實際性能。您的性能度量標準應(yīng)該明確體現(xiàn)應(yīng)用程序的重要性能特征。請努力避免無法準確度量的模糊或不完整的目標,例如,“應(yīng)用程序必須快速運行”或“應(yīng)用程序必須快速加載”。您需要了解應(yīng)用程序的性能和可伸縮性目標,以便您可以設(shè)法滿足這些目標并且圍繞它們來規(guī)劃您的測試。請確保您的目標是可度量的和可驗證的。
定義良好的性能度量標準使您可以準確跟蹤應(yīng)用程序的性能,以便您可以確定應(yīng)用程序是否能夠滿足它的性能目標。這些度量標準應(yīng)該包括在應(yīng)用程序測試計劃中,以便可以在應(yīng)用程序的測試階段度量它們。
本節(jié)重點討論與智能客戶端應(yīng)用程序相關(guān)的特定性能目標的定義。如果您還要設(shè)計和生成客戶端應(yīng)用程序?qū)⑾牡木W(wǎng)絡(luò)服務(wù),則您還需要為這些服務(wù)定義適當?shù)男阅苣繕恕T诖饲闆r下,您應(yīng)該確保考慮整個系統(tǒng)的性能要求,以及應(yīng)用程序各個部分的性能與其他部分以及整個系統(tǒng)之間存在怎樣的關(guān)系。
考慮用戶的觀點
當您為智能客戶端應(yīng)用程序確定合適的性能目標時,您應(yīng)該仔細考慮用戶的觀點。對于智能客戶端應(yīng)用程序而言,性能與可用性和用戶感受有關(guān)。例如,只要用戶能夠繼續(xù)工作并且獲得有關(guān)操作進度的足夠反饋,用戶就可以接受漫長的操作。
在確定要求時,將應(yīng)用程序的功能分解為多個使用情景或使用案例通常是有用的。您應(yīng)該識別對于實現(xiàn)特定性能目標而言關(guān)鍵且必需的使用案例和情景。應(yīng)該將許多使用案例所共有且經(jīng)常執(zhí)行的任務(wù)設(shè)計得具有較高性能。同樣,如果任務(wù)要求用戶全神貫注并且不允許用戶從其切換以執(zhí)行其他任務(wù),則需要提供優(yōu)化的且有效的用戶體驗。如果任務(wù)不太經(jīng)常使用且不會阻止用戶執(zhí)行其他任務(wù),則可能無須進行大量調(diào)整。
對于您識別的每個性能敏感型任務(wù),您都應(yīng)該精確地定義用戶的操作以及應(yīng)用程序的響應(yīng)方式。您還應(yīng)該確定每個任務(wù)使用的網(wǎng)絡(luò)和客戶端資源或組件。該信息將影響性能目標,并且將驅(qū)動對性能進行度量的測試。
可用性研究提供了非常有價值的信息源,并且可能大大影響性能目標的定義。正式的可用性研究在確定用戶如何執(zhí)行他們的工作、哪些使用情景是共有的以及哪些不是共有的、用戶經(jīng)常執(zhí)行哪些任務(wù)以及從性能觀點看來應(yīng)用程序的哪些特征是重要的等方面可能非常有用。如果您要生成新的應(yīng)用程序,您應(yīng)該考慮提供應(yīng)用程序的原型或模型,以便可以執(zhí)行基本的可用性測試。
考慮應(yīng)用程序操作環(huán)境
對應(yīng)用程序的操作環(huán)境進行評估是很重要的,因為這可能對應(yīng)用程序施加必須在您制定的性能目標中予以反映的約束。
位于網(wǎng)絡(luò)上的服務(wù)可能對您的應(yīng)用程序施加性能約束。例如,您可能需要與您無法控制的 Web 服務(wù)進行交互。在這種情況下,需要確定該服務(wù)的性能,并且確定這是否將對客戶端應(yīng)用程序的性能產(chǎn)生影響。
您還應(yīng)該確定任何相關(guān)服務(wù)和組件的性能如何隨著時間的變化而變化。某些系統(tǒng)會經(jīng)受相當穩(wěn)定的使用,而其他系統(tǒng)則會在一天或一周的特定時間經(jīng)受變動極大的使用。這些區(qū)別可能在關(guān)鍵時間對應(yīng)用程序的性能造成不利影響。例如,提供應(yīng)用程序部署和更新服務(wù)的服務(wù)可能會在星期一早上 9 點緩慢響應(yīng),因為所有用戶都在此時升級到應(yīng)用程序的最新版本。
另外,還需要準確地對所有相關(guān)系統(tǒng)和組件的性能進行建模,以便可以在嚴格模擬應(yīng)用程序的實際部署環(huán)境的環(huán)境中測試您的應(yīng)用程序。對于每個系統(tǒng),您都應(yīng)該確定性能概況以及最低、平均和最高性能特征。然后,您可以在定義應(yīng)用程序的性能要求時根據(jù)需要使用該數(shù)據(jù)。
您還應(yīng)該仔細考慮用于運行應(yīng)用程序的硬件。您將需要確定在處理器、內(nèi)存、圖形功能等方面的目標硬件配置,或者至少確定一個如果得不到滿足則無法保證性能的最低配置。
通常,應(yīng)用程序的業(yè)務(wù)操作環(huán)境將規(guī)定一些更為苛刻的性能要求。例如,執(zhí)行實時股票交易的應(yīng)用程序?qū)⑿枰獔?zhí)行這些交易并及時顯示所有相關(guān)數(shù)據(jù)。
性能調(diào)整過程
對應(yīng)用程序進行性能調(diào)整是一個迭代過程。該過程由一些重復(fù)執(zhí)行直至應(yīng)用程序滿足其性能目標的階段組成。(請參見圖 8.2。)
圖 8.2:性能調(diào)整過程
正如圖 8.2 所闡明的,性能調(diào)整要求您完成下列過程:
? |
建立基準。在您開始針對性能調(diào)整應(yīng)用程序時,您必須具有與性能目標、目標和度量標準有關(guān)的定義良好的基準。這可能包括應(yīng)用程序工作集大小、加載數(shù)據(jù)(例如,目錄)的時間、事務(wù)持續(xù)時間等等。 |
? |
收集數(shù)據(jù)。您將需要通過針對您已經(jīng)定義的性能目標度量應(yīng)用程序的性能,來對應(yīng)用程序性能進行評價。性能目標應(yīng)該體現(xiàn)特定的且可度量的度量標準,以使您可以在任何時刻量化應(yīng)用程序的性能。要使您可以收集性能數(shù)據(jù),您可能必須對應(yīng)用程序進行規(guī)范,以便可以發(fā)布和收集必需的性能數(shù)據(jù)。下一節(jié)將詳細討論您可以用來完成這一工作的一些選項。 |
? |
分析結(jié)果。在收集應(yīng)用程序的性能數(shù)據(jù)之后,您將能夠通過確定哪些應(yīng)用程序功能要求最多的關(guān)注,來區(qū)分性能調(diào)整工作的輕重緩急。此外,您可以使用該數(shù)據(jù)來確定任何性能瓶頸的位置。通常,您將只能夠通過收集更詳細的性能數(shù)據(jù)來確定瓶頸的確切位置:例如,通過使用應(yīng)用程序規(guī)范。性能分析工具可能幫助您識別瓶頸。 |
? |
調(diào)整應(yīng)用程序。在已經(jīng)識別瓶頸之后,您可能需要修改應(yīng)用程序或其配置,以便嘗試解決問題。您應(yīng)該致力于將更改降低至最低限度,以便可以確定更改對應(yīng)用程序性能的影響。如果您同時進行多項更改,可能難以確定每項更改對應(yīng)用程序的總體性能的影響。 |
? |
測試和度量。在更改應(yīng)用程序或其配置之后,您應(yīng)該再次測試它以確定更改具有的效果,并且使新的性能數(shù)據(jù)得以收集。性能工作通常要求進行體系結(jié)構(gòu)或其他具有較高影響的更改,因此徹底的測試是很關(guān)鍵的。您的應(yīng)用程序測試計劃應(yīng)該針對預(yù)料到的所有情況,在配置了適當硬件和軟件的客戶計算機上演習(xí)應(yīng)用程序所實現(xiàn)的完整范圍的功能。如果您的應(yīng)用程序使用網(wǎng)絡(luò)資源,則應(yīng)該加載這些資源,以便您可以獲得有關(guān)應(yīng)用程序在此類環(huán)境中所具有的性能的準確度量。 |
上述過程將使您可以通過針對特定目標度量應(yīng)用程序的總體性能,來重點解決特定的性能問題。
性能工具
您可以使用許多工具來幫助您收集和分析應(yīng)用程序的性能數(shù)據(jù)。本節(jié)中介紹的每種工具都具有不同的功能,您可以使用這些功能來度量、分析和查找應(yīng)用程序中的性能瓶頸。
注除了這里介紹的工具以外,您還可以使用其他一些選項和第三方工具。有關(guān)其他日志記錄和異常管理選項的說明,請參閱Exception Management Architecture Guide,網(wǎng)址為:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/exceptdotnet.asp
在決定哪些工具最適合您的需要之前,您應(yīng)該仔細考慮您的確切要求。
使用性能日志和警報
性能日志和警報是作為 Windows 操作系統(tǒng)的一部分發(fā)行的一種管理性能監(jiān)控工具。它依靠由各種 Windows 組件、子系統(tǒng)和應(yīng)用程序發(fā)布的性能計數(shù)器,使您可以跟蹤資源使用情況以及針對時間以圖形方式繪制它們。
您可以使用 Performance Logs and Alerts 來監(jiān)控標準的性能計數(shù)器(例如,內(nèi)存使用情況或處理器使用情況),或者您可以定義您自己的自定義計數(shù)器來監(jiān)控應(yīng)用程序特定的活動。
.NET CLR 提供了許多有用的性能計數(shù)器,它們使您可以洞察應(yīng)用程序性能的好壞。關(guān)系比較大的一些性能對象是:
? |
.NET CLR 內(nèi)存。提供有關(guān)托管 .NET 應(yīng)用程序內(nèi)存使用情況的數(shù)據(jù),包括應(yīng)用程序正在使用的內(nèi)存數(shù)量以及對未使用的對象進行垃圾回收所花費的時間。 |
? |
.NET CLR 加載。提供有關(guān)應(yīng)用程序正在使用的類和應(yīng)用程序域的數(shù)量的數(shù)據(jù),并且提供有關(guān)它們的加載和卸載速率的數(shù)據(jù)。 |
? |
.NET CLR 鎖和線程。提供與應(yīng)用程序內(nèi)使用的線程有關(guān)的性能數(shù)據(jù),包括線程個數(shù)以及試圖同時對受保護的資源進行訪問的線程之間的爭用率。 |
? |
.NET CLR 網(wǎng)絡(luò)。提供與通過網(wǎng)絡(luò)發(fā)送和接收數(shù)據(jù)有關(guān)的性能計數(shù)器,包括每秒發(fā)送和接收的字節(jié)數(shù)以及活動連接的個數(shù)。 |
? |
.NET CLR 異常。提供有關(guān)應(yīng)用程序所引發(fā)和捕獲的異常個數(shù)的報告。 |
有關(guān)上述計數(shù)器、它們的閾值、要度量的內(nèi)容以及如何度量它們的詳細信息,請參閱 Improving .NET Application Performance and Scalability 的第 15 章“Measuring .NET Application Performance”中的“CLR and Managed Code”部分,網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt15.asp。
您的應(yīng)用程序還可以提供您可以通過使用性能日志和警報輕松監(jiān)控的、應(yīng)用程序特定的性能計數(shù)器。您可以像以下示例所顯示的那樣,定義自定義性能計數(shù)器:
[C#]
PerformanceCounter counter = new PerformanceCounter( "Category",
"CounterName", false );
[Visual Basic .NET]
Dim counter As New PerformanceCounter("Category", "CounterName", False)
在創(chuàng)建性能計數(shù)器對象之后,您可以為您的自定義性能計數(shù)器指定類別,并將所有相關(guān)計數(shù)器保存在一起。PerformanceCounter 類在 System.Diagnostics 命名空間中定義,該命名空間中還定義了其他一些可用于讀取和定義性能計數(shù)器和類別的類。有關(guān)創(chuàng)建自定義性能計數(shù)器的詳細信息,請參閱知識庫中編號為 317679 的文章“How to create and make changes to a custom counter for the Windows Performance Monitor by using Visual Basic .NET”,網(wǎng)址為:http://support.microsoft.com/default.aspx?scid=kb;en-us;317679。
注要注冊性能計數(shù)器,您必須首先注冊該類別。您必須具有足夠的權(quán)限才能注冊性能計數(shù)器類別(它可能影響您部署應(yīng)用程序的方式)。
規(guī)范
您可以使用許多工具和技術(shù)來幫助您對應(yīng)用程序進行規(guī)范,并且生成度量應(yīng)用程序性能所需的信息。這些工具和技術(shù)包括:
CLR Profiler
CLR Profiler 是 Microsoft 提供的一種內(nèi)存分析工具,并且可以從 MSDN 下載。它使您能夠查看應(yīng)用程序進程的托管堆以及調(diào)查垃圾回收器的行為。使用該工具,您可以獲取有關(guān)應(yīng)用程序的執(zhí)行、內(nèi)存分配和內(nèi)存消耗的有用信息。這些信息可以幫助您了解應(yīng)用程序的內(nèi)存使用方式以及如何優(yōu)化應(yīng)用程序的內(nèi)存使用情況。
CLR Profiler 可從 http://msdn.microsoft.com/netframework/downloads/tools/default.aspx 獲得。有關(guān)如何使用 CLR Profiler 工具的詳細信息,另請參閱“How to use CLR Profiler”,網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto13.asp?frame=true。
CLR Profiler 在日志文件中記錄內(nèi)存消耗和垃圾回收器行為信息。然后,您可以使用一些不同的圖形視圖,通過 CLR Profiler 來分析該數(shù)據(jù)。一些比較重要的視圖是:
? |
Allocation Graph。顯示有關(guān)對象分配方式的調(diào)用堆棧。您可以使用該視圖來查看方法進行的每個分配的系統(tǒng)開銷,隔離您不希望發(fā)生的分配,以及查看方法可能進行的過度分配。 |
? |
Assembly, Module, Function, and Class Graph。顯示哪些方法造成了哪些程序集、函數(shù)、模塊或類的加載。 |
? |
Call Graph。使您可以查看哪些方法調(diào)用了其他哪些方法以及相應(yīng)的調(diào)用頻率。您可以使用該圖表來確定庫調(diào)用的系統(tǒng)開銷,以及調(diào)用了哪些方法或?qū)μ囟ǚ椒ㄟM行了多少個調(diào)用。 |
? |
Time Line。提供了有關(guān)應(yīng)用程序執(zhí)行的基于文本的、按時間順序的層次結(jié)構(gòu)視圖。使用該視圖可以查看分配了哪些類型以及這些類型的大小。您還可以使用該視圖查看方法調(diào)用使得哪些程序集被加載,并且分析您不希望發(fā)生的分配。您可以分析完成器的使用情況,并且識別尚未實現(xiàn)或調(diào)用 Close 或 Dispose 從而導(dǎo)致瓶頸的方法。 |
您可以使用 CLR Profiler.exe 來識別和隔離與垃圾回收有關(guān)的問題。這包括內(nèi)存消耗問題(例如,過度或未知的分配、內(nèi)存泄漏、生存期很長的對象)以及在執(zhí)行垃圾回收時花費的時間的百分比。
注有關(guān)如何使用 CLR Profiler 工具的詳細信息,請參閱“Improving .NET Application Performance and Scalability”,網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto13.asp?frame=true。
小結(jié)
要完全實現(xiàn)智能客戶端應(yīng)用程序的潛能,您需要在應(yīng)用程序的設(shè)計階段仔細考慮性能問題。通過在早期階段解決這些性能問題,您可以在應(yīng)用程序設(shè)計過程中控制成本,并減小在開發(fā)周期的后期陷入性能問題的可能性。
本章分析了許多不同的技術(shù),您可以在規(guī)劃和設(shè)計智能客戶端應(yīng)用程序時使用這些技術(shù),以確保優(yōu)化它們的性能。本章還考察了您可以用來確定智能客戶端應(yīng)用程序內(nèi)的性能問題的一些工具和技術(shù)。
參考資料
有關(guān)詳細信息,請參閱以下內(nèi)容:
轉(zhuǎn)到原英文頁面