国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

使用.NET Profiler API檢查并優(yōu)化程序的內(nèi)存使用

2019-11-18 12:03:27
字體:
供稿:網(wǎng)友

  運行在 Windows 下面的程序分配內(nèi)存以便表現(xiàn)所需要的、不同類型的資源。可以將這些分配當(dāng)作用來封裝程序所需要的內(nèi)存和其他任何資源狀態(tài)的對象。
  
  應(yīng)用程序正確運行時,系統(tǒng)將釋放被使用的資源和內(nèi)存,以便讓系統(tǒng)中的其他程序使用。但有時候,假如應(yīng)用程序出現(xiàn)錯誤,則資源狀態(tài)或內(nèi)存(或者這二者)都不會被正確釋放,這就會造成資源或內(nèi)存泄漏。這些錯誤可能是很難識別的。垃圾回收器 (GC) 負責(zé)確保程序所分配的、用于完成任務(wù)的內(nèi)存能夠在不需要開發(fā)人員關(guān)注它的情況下被釋放。
  
  對垃圾回收了解得越多,就越能更好地構(gòu)造程序與之配合使用。.NET 中的對象是從稱為托管堆的一片內(nèi)存中分配來的。堆被描述為托管是因為您向它申請內(nèi)存后,垃圾回收器會負責(zé)執(zhí)行清理工作。這似乎需要很多開銷,因為垃圾回收器必須跟蹤在 .NET 公共語言運行庫 (CLR) 中所分配的每個對象,但實際上它工作得很有效率。
  
  對象可以是小型對象,可以包含少量整數(shù)或更大的數(shù)據(jù),也可以包含數(shù)據(jù)庫連接和很多狀態(tài)信息。對象可以是獨立的,也可以在內(nèi)部包含或使用其他對象。GC 的工作是確定什么時候應(yīng)當(dāng)回收對象,以便釋放內(nèi)存供其他程序使用,當(dāng)它認為它已被裝滿時就會對可以刪除的對象作標(biāo)記,然后從托管堆中將它們刪除。當(dāng)垃圾回收器試圖分配新的對象、卻發(fā)現(xiàn)托管堆沒有更多的可用內(nèi)存時,垃圾回收器就會認為它已被裝滿。GC 試圖分配內(nèi)存但確定它已被裝滿時,它將嘗試清理已為您的應(yīng)用程序分配的某些內(nèi)存,以便為新對象騰出空間。
  
  GC 以略微不同的方式看待您的對象,并在決定什么時候回收它認為不再有用的對象時考慮到這些對象的差異。它這樣做的一個方法是,它有一組根對象,用來確定哪些對象可以被回收。假如對對象的引用大體上屬于如下分類中的某一個,則該引用就被看作是根:全局或靜態(tài)對象指針、線程的堆棧上的所有局部變量和參數(shù)對象指針、或包含托管堆中的對象的指針的任何 CPU 寄存器。假如對象的引用是根引用,那么它可能有或可能沒有與它關(guān)聯(lián)的、還會在垃圾回收后幸存的子對象。GC 首先找到根對象,然后沿著引用找到被根引用的其他對象,以便避免回收這些對象。
  
  如圖 1 所示,托管堆中有四個被分配的對象:(S)mall、(L)arge、(F)inalized 和 (R)eferenced。假設(shè)每個對象通過其主要特征(例如,小型對象都不會包含引用或其他組合)來標(biāo)識自己。在堆中分配這些對象時,它們將相互緊鄰地放在內(nèi)存中。我也有一個位于 (G)lobal 范圍的根引用,它包含對 Z 的引用。
  
 使用.NET Profiler API檢查并優(yōu)化程序的內(nèi)存使用(圖一)

  GC 開始垃圾回收時,它首先假設(shè)所有對象都是不必要的,直到這些對象被證實是需要的為止。對象基本上通過它“熟悉”誰或引用了誰,或誰引用了它或熟悉它,來證實自己是必要的。對于 GC,根引用為誰熟悉誰提供了起點。GC 從根對象開始沿著對象層次結(jié)構(gòu)檢查引用情況,以確定對象是否是可到達的,或是否有可能被另一個對象使用。假如對象被證實是可到達的,則它不是該垃圾回收周期的處理對象。假如對象被證實無法從任何引用到達它,則 GC 將把該對象標(biāo)記為可回收,然后它會被丟棄。GC 使用“標(biāo)記和壓縮”方法,這意味著一旦 GC 確定對象是垃圾,則 GC 的另一個部分將刪除無法到達的對象,并將壓縮堆中的空間以確保分配將繼續(xù)非常快速地進行。
  
  GC 以代的方式看待回收周期中所涉及的對象。每當(dāng)對象被認為是可到達的時,它就會被提升到下一代。這意味著,引用您的對象的對象越多,或您的對象的操作范圍越大,它的存活時間就越長。GC 當(dāng)前最多有三代,從 0 到 2。第 0 代通常填充較小、短期使用的對象,并且回收它們的次數(shù)最多。這意味著,假如您有小型或很少使用的對象,則它們將被頻繁地回收。第 1 代和第 2 代是壽命更長和被更頻繁訪問的對象的儲存庫,因此被回收的頻率更低。GC 中一個基本假設(shè)是,您的程序中有更小、壽命更短的對象,更頻繁地清理它們對您有好處。理解這一點很重要,因為您設(shè)計系統(tǒng)的方式會對您使用多少內(nèi)存和占用內(nèi)存多長時間有巨大的影響,這是由于您的工作集將是大型的工作集。內(nèi)存使用量越大,應(yīng)用程序性能將降低得越多。
  
  85,000 字節(jié)以下的對象被認為是小型對象,并且從托管堆的主要部分直接分配。超過 85,000 字節(jié)的對象從托管堆的非凡部分(稱為大型對象堆)分配。托管堆對待小型和大型對象的方式有兩個主要差異。首先,小型對象在被壓縮時將移到托管堆內(nèi);而大型對象則不是這樣。其次,大型對象總是被當(dāng)作第 2 代的一部分,而小型對象通常被當(dāng)作第 0 代的一部分。假如您分配了很多短壽命的大型對象,這將造成第 2 代被更頻繁地回收。由于從第 0 代到第 2代越往后的回收成本越高,這將有損應(yīng)用程序的性能。
  
  我想討論的垃圾回收的最后一個方面是終結(jié) (finalization) 的概念。當(dāng)對象被 GC 回收時,終結(jié)幫助開發(fā)人員釋放他們在其對象中使用的資源。對象需要實現(xiàn) Finalize 方法才能完成該操作。當(dāng)對象要被銷毀時,GC 將調(diào)用 Finalize 方法,以便答應(yīng)對象清理它的內(nèi)部資源和狀態(tài)。在 C# 和托管 C++ 中,F(xiàn)inalize 方法實際上偽裝在析構(gòu)函數(shù)的語法 (~Object) 中,這里的 Finalize 方法與純 C++ 中的 Finalize 方法之間的重大差異是,在 C# 和托管 C++ 中,只有當(dāng) GC 清理對象時才調(diào)用該方法,而在純 C++ 的析構(gòu)函數(shù)中,當(dāng)對象脫離范圍時才會調(diào)用該方法。將 Finalize 方法添加到您的對象中意味著它將總是被 GC 調(diào)用,但要小心,因為將 Finalize 方法添加到對象中時,該對象將總是會在對第一代的垃圾回收后幸存下來。因此,所有終結(jié)對象的壽命會更長。由于試圖讓 GC 盡可能有效地執(zhí)行清理,因此,只有當(dāng)您有非托管資源需要清理或者在對象創(chuàng)建成本高昂的非凡情況下(對象池),才應(yīng)當(dāng)使用終結(jié)。
  
  讓我們返回圖 1 中的原始示例,該示例有一個托管堆,其中包含四個對象和一個根引用。假如在這個時候發(fā)生垃圾回收(這是由于這時不滿足啟動垃圾回收的條件,而開發(fā)人員手動干預(yù)造成的),結(jié)果是 (S)mall 對象將被當(dāng)作垃圾回收。
  
  大型對象將在該垃圾回收后幸存下來,因為大型對象被指派為第 2 代。被終結(jié)的對象被 GC 注重到,并且將調(diào)用 Finalize 方法,但是對象本身仍將保留下來,直到進行下一次垃圾回收為止(在某些情形下可能會更長)。包含根引用 G 的對象將保留下來;因為它是根引用,是可到達的。
  
  現(xiàn)在,讓我們假設(shè)下一次發(fā)生的垃圾回收針對的是第 0 到第 2 代(可以通過調(diào)用 System.GC.Collect 方法并將 2 作為參數(shù)來完成該操作)。(L)arge 對象將在第 2 代清理期間被回收,而 (F)inalized 對象在第 0 代回收期間被回收,這是因為 Finalize 已被調(diào)用并且已在回收開始之前結(jié)束操作。只有包含全局引用的對象仍然存在,因而會在應(yīng)用程序生存期內(nèi)保留下來。
  
  良好的內(nèi)存使用率
  GC 負責(zé)處理內(nèi)存泄漏,但它不能防止內(nèi)存保留。作為開發(fā)人員,您可以控制您的對象的生存期。假如可以減少應(yīng)用程序的工作集,則性能將有所提高。假如您的應(yīng)用程序被設(shè)計為有很多對象長時間存活,則可能會有內(nèi)存泄漏。即使最后清理了內(nèi)存,仍然會有損性能,所以知道您的對象存活多長時間是值得的。
  
  GC 可以提供很大幫助,但它只能處理我討論過的一種原始類型的泄漏。資源泄漏仍然是個問題,但假如將非托管資源包裝在終結(jié)類中,GC 仍然可以幫助您確保正確處置它們。最好對對象實現(xiàn) Close 或 Dispose 方法,以便在使用完對象時資源可以盡可能早得到清理,而不用等待 GC 來清理它們(在您停止使用對象后,等待 GC 清理它們可能需要很長時間)。假如您對使用非托管資源的類實現(xiàn)了 Finalize,并且正在使用托管堆,則可以相當(dāng)安全地避免真正的泄漏。當(dāng)然,這并不意味著您應(yīng)當(dāng)讓應(yīng)用程序的工作集很龐大,因為這仍然會有損性能。
  
  PRofiler API 概述
  為了說明應(yīng)用程序使用了多少內(nèi)存,以及對象存在了多久,我開發(fā)了一個稱為 MemoryUsage 的應(yīng)用程序。MemoryUsage 有兩個不同的部分。第一部分編寫為 C# 應(yīng)用程序,它將啟動要監(jiān)視的進程,并在目標(biāo)進程中設(shè)置一個環(huán)境變量,以指示 CLR 應(yīng)當(dāng)加載 .NET 分析器 (profiler)。第二部分編寫為基于 C++ 的 .NET 分析器,該分析器名為 MemProfiler,CLR 將通過環(huán)境變量中的信息加載它。.NET 分析器是使用作為 CLR 的一部分提供的 Profiler API 來編寫的,它答應(yīng)分析器作為被監(jiān)視的進程的一部分運行,并在發(fā)生某些事件時接收通知。當(dāng)應(yīng)用程序執(zhí)行時,它為您提供各種通知。為了從 CLR 接收這些通知,您要提供一個 Profiler API 中指定的回調(diào)接口 (ICorProfilerCallback),然后,當(dāng)各種事件發(fā)生時,CLR 將調(diào)用這個回調(diào)接口的方法(參見圖 2)。
  
 使用.NET Profiler API檢查并優(yōu)化程序的內(nèi)存使用(圖二)

  下面是需要注重的主要分析器回調(diào)方法:RuntimeSuspendStarted、RuntimeSuspendFinished、RuntimeResumeStarted、ObjectAllocated、ObjectsAllocatedByClass、MovedReferences、RootReferences 和 ObjectReferences。
  
  假如不熟悉 Profiler API,可以閱讀 Profiler.doc(位于 Visual Studio .NET 安裝目錄下面的 /FrameworkSDK/Tool Developers Guide/docs 文件夾中),來了解某些更深入的信息。
  
  使用分析器時有幾件事情要考慮到,包括線程安全和同步,以及分析器對性能的影響。Profiler API 實際上答應(yīng)您將它作為 CLR 的一部分運行,這樣,因為多個線程將調(diào)用您的分析器,所以您必須知道存在同步問題。Microsoft 提供的 Profiler API 規(guī)范聲明:回調(diào)不會被序列化。這就需要由開發(fā)人員自己來正確保護他的代碼,方法是創(chuàng)建線程安全的數(shù)據(jù)結(jié)構(gòu),并在一旦需要防止多個線程并行訪問代碼時鎖定分析器代碼。
  
  我需要使對對象跟蹤系統(tǒng)以及在我

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 类乌齐县| 六枝特区| 上林县| 阿克陶县| 钟祥市| 特克斯县| 兰州市| 田东县| 琼海市| 苏州市| 宁海县| 浠水县| 红安县| 类乌齐县| 黎城县| 抚顺县| 连城县| 郧西县| 胶南市| 邵东县| 汝州市| 桃园市| 广水市| 确山县| 若尔盖县| 鹤峰县| 喀喇沁旗| 张北县| 昭通市| 宁德市| 石屏县| 侯马市| 平果县| 阜新| 江山市| 丰县| 噶尔县| 上林县| 三明市| 榆社县| 建始县|