2013-03-29 68 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

最佳答案,很干净的帮手。 –

相关问题