是否有必要处理每个ManagementObject?
我注意到ManagementObject
是IDisposable
,但它也是从ManagementClass.GetInstances()
和ManagementObjectSearcher.Get()
,这是否意味着我需要处理遇到的每个对象?
像这样:
ManagementObject ret; foreach(ManagementObject mo in searcher.Get()) { if( IsWhatIWant(mo) ) ret = mo; else mo.Dispose(); }
进一步混淆了这一点: ManagementBaseObject
中有一个错误,它没有正确实现IDisposable
(请参阅Using子句无法调用Dispose? )所以你需要自己调用它,或者使用一个正确调用它的包装器。
这很烦人,因为我有很多ManagementObjectCollections
。
这很烦人,因为我有很多ManagementObjectCollections。
这与调用Dispose()无关,它只释放底层的非托管COM对象。 ManagementObjectCollection是托管类,它的实例是垃圾回收。 这是自动的,你只能通过调用GC.Collect()来帮助。 你的程序可能只是创建了很多System.Management对象,可能是因为这是它唯一做过的事情。 在我的机器上安装的当前版本的.NET 3.5SP1和.NET 4.5中修复了引用的错误。
因此,如果您没有.NET的修补版本,那么您不仅会在GC堆上看到很多System.Management对象,您的进程也会消耗大量非托管内存。 如果垃圾收集器运行频率不够,那么可能导致程序与OOM崩溃。 你没有提到它是一种失败模式,因此没有强烈表明你有一个真正的问题。
GC堆的第0代的初始大小是2兆字节,它可以增长到8+兆字节。 这是很多 ManagementObjectCollections对象,它是一个非常小的对象,在32位模式下只占用24个字节。 实际的集合是不受管理的。 使用Perfmon.exe或内存分析器检查垃圾收集器是否运行得足够频繁。 如果没有,那么请关注程序的VM大小。 如果那是气球膨胀然后在查询循环中使用计数器并且当它足够高时调用GC.Collect()是一个可行的解决方法。 仔细考虑从内存分析器中获取的信息,它会因为错误的原因而烦恼。
我创建了一个帮助对象来处理所有创建的管理对象:
public class ManagementObjectDisposer : IDisposable { private List disposables = new List (); /// /// Workaround to dispose ManagementBaseObject properly. /// See http://stackoverflow.com/questions/11896282 /// /// 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 disposable) where T : IDisposable { disposables.Add(disposable); return disposable; } /// /// Helper for ManagementObjectSearcher with adding all objects to the disposables. /// /// The query string. public IEnumerable Search(string query) { ManagementObjectSearcher searcher = this.Add(new ManagementObjectSearcher(query)); return EnumerateCollection(searcher.Get()); } /// /// Helper for adding ManagementObjectCollection and enumerating it. /// public IEnumerable 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
。
实际上代码来自:
http://referencesource.microsoft.com/#System.Management/managementobjectcollection.cs
以及Microsoft Symbol Server中的符号
http://msdl.microsoft.com/download/symbols
Imply ManagementObjectCollection是IDisposable,这意味着它出于某种原因使用非托管资源或者使用IDisposable接口时使用不正确。