熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

使用.NET Profiler API檢查並優化程序的內存使用

2013-11-23 19:34:43  來源: Java核心技術 

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

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

  下面是需要注意的主要分析器回調方法RuntimeSuspendStartedRuntimeSuspendFinishedRuntimeResumeStartedObjectAllocatedObjectsAllocatedByClassMovedReferencesRootReferences 和 ObjectReferences
  
  如果不熟悉 Profiler API可以閱讀 Profilerdoc(位於 Visual Studio NET 安裝目錄下面的 \FrameworkSDK\Tool Developers Guide\docs 文件夾中)來了解某些更深入的信息
  
  使用分析器時有幾件事情要考慮到包括線程安全和同步以及分析器對性能的影響Profiler API 實際上允許您將它作為 CLR 的一部分運行這樣因為多個線程將調用您的分析器所以您必須知道存在同步問題Microsoft 提供的 Profiler API 規范聲明回調不會被序列化這就需要由開發人員自己來正確保護他的代碼方法是創建線程安全的數據結構並在一旦需要防止多個線程並行訪問代碼時鎖定分析器代碼
  
  我需要使對對象跟蹤系統以及在我
From:http://tw.wingwit.com/Article/program/Java/hx/201311/27123.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.