2013-03-29 105 views
4

我注意到ManagementObjectIDisposable,但它也從ManagementClass.GetInstances()ManagementObjectSearcher.Get()返回,這是否意味着我需要處理遇到的每個對象?是否有必要處理每個ManagementObject?

像這樣:

ManagementObject ret; 
foreach(ManagementObject mo in searcher.Get()) { 
    if(IsWhatIWant(mo)) ret = mo; 
    else mo.Dispose(); 
} 

進一步混淆這樣的:有一個在ManagementBaseObject有一個錯誤:不正確地實現IDisposable(見Using clause fails to call Dispose?),所以你需要自行調用它,或者用它周圍的包裝,不正確地調用它。

這是令人討厭的,因爲我有這麼多的ManagementObjectCollections左右。

+1

'foreach'通過'ManagementObjectCollections'創建'ManagementObjectEnumerator'並且應該被處置。 ugh – xmedeko

回答

3

這是令人討厭的,因爲我有這麼多的ManagementObjectCollections。

與調用Dispose()無關,只能釋放底層的非託管COM對象。 ManagementObjectCollection是一個託管類,它的實例被垃圾收集。這是自動的,你只能通過調用GC.Collect()來獲得幫助。你的程序可能只是創建了很多System.Management對象,可能是因爲這是它唯一做過的事情。引用的bug在我的機器上安裝的當前版本的.NET 3.5SP1和.NET 4.5中得到修復。

所以如果你沒有.NET的補丁版本,那麼你不僅會在GC堆上看到很多System.Management對象,你的進程也會消耗大量的非託管內存。如果垃圾收集器運行不夠頻繁,那可能導致程序與OOM崩潰。你沒有提到這是一個失敗模式,所以沒有強烈表明你有一個真正的問題。

GC堆的第0代的初始大小是2兆字節,它可以增長到8+兆字節。這是ManagementObjectCollections對象的批次,它是一個非常小的對象,在32位模式下僅佔用24個字節。實際的收集是不受管理的。使用Perfmon.exe或您的內存分析器來檢查垃圾收集器運行頻率是否足夠。如果沒有,那就留意你的程序的虛擬機大小。如果這是膨脹,那麼在查詢循環中使用計數器並在足夠高時調用GC.Collect()是一種可行的解決方法。仔細查看你從內存分析器中獲得的信息,可能會因爲錯誤的原因而出現問題。

+0

我的問題不是關於內存 - 而是底層的非託管COM對象 - 沒有任何文檔清楚說明* *調用ManagementObject.Dispose()會產生什麼後果。 – Dai

1

我創建了一個輔助對象處置所有創建的管理對象:

public class ManagementObjectDisposer : IDisposable 
{ 
    private List<IDisposable> disposables = new List<IDisposable>(); 

    /// <summary> 
    /// Workaround to dispose ManagementBaseObject properly. 
    /// See http://stackoverflow.com/questions/11896282 
    /// </summary> 
    /// <param name="disposable"></param> 
    public static void DisposeOne(IDisposable disposable) 
    { 
     ManagementBaseObject mbo = disposable as ManagementBaseObject; 
     if (mbo != null) 
      mbo.Dispose(); 
     else 
      disposable.Dispose(); 
    } 

    public void Dispose() 
    { 
     Exception firstException = null; 
     foreach (IDisposable d in Enumerable.Reverse(disposables)) 
     { 
      try 
      { 
       DisposeOne(d); 
      } 
      catch (Exception ex) 
      { 
       if (firstException == null) 
        firstException = ex; 
       else 
        cvtLogger.GetLogger(this).Error($"Swallowing exception when disposing: {d.GetType()}", ex); 
      } 
     } 
     disposables.Clear(); 
     if (firstException != null) 
      throw firstException; 
    } 

    public T Add<T>(T disposable) where T : IDisposable 
    { 
     disposables.Add(disposable); 
     return disposable; 
    } 

    /// <summary> 
    /// Helper for ManagementObjectSearcher with adding all objects to the disposables. 
    /// </summary> 
    /// <param name="query">The query string.</param> 
    public IEnumerable<ManagementBaseObject> Search(string query) 
    { 
     ManagementObjectSearcher searcher = this.Add(new ManagementObjectSearcher(query)); 
     return EnumerateCollection(searcher.Get()); 
    } 

    /// <summary> 
    /// Helper for adding ManagementObjectCollection and enumerating it. 
    /// </summary> 
    public IEnumerable<ManagementBaseObject> EnumerateCollection(ManagementObjectCollection collection) 
    { 
     this.Add(collection); 
     ManagementObjectCollection.ManagementObjectEnumerator enumerator = this.Add(collection.GetEnumerator()); 
     while (enumerator.MoveNext()) 
      yield return this.Add(enumerator.Current); 
    } 
} 

只是用它喜歡:

using (var moDisposer = new ManagementObjectDisposer()) 
{ 
    foreach (var mobj = moDisposer.Search("SELECT * FROM Win32_Processor") 
     Console.WriteLine(mobj["DeviceID"]); 
} 

注:ManagementClass.GetInstances()很容易地添加到ManagementObjectDisposer,太。

+0

最佳答案,很乾淨的幫手。 –

相關問題