【问题标题】:Is it necessary to dispose every ManagementObject?是否有必要处置每个 ManagementObject?
【发布时间】:2013-03-29 05:08:21
【问题描述】:

我注意到ManagementObjectIDisposable,但它也是从ManagementClass.GetInstances()ManagementObjectSearcher.Get() 返回的,这是否意味着我需要处理遇到的每个对象?

像这样:

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

【问题讨论】:

  • foreachManagementObjectCollections 创建 ManagementObjectEnumerator 并且也应该被处理掉。呃

标签: c# wmi


【解决方案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

【讨论】:

    【解决方案2】:

    这很烦人,因为我周围有很多 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() 是一种可行的解决方法。小心你从内存分析器中得到的信息,它可能会因为错误的原因而烦人。

    【讨论】:

    • 我的问题不是关于内存 - 而是底层的非托管 COM 对象 - 没有任何文档清楚说明 not 调用 ManagementObject.Dispose() 的后果是什么.
    【解决方案3】:

    其实代码来自:

    http://referencesource.microsoft.com/#System.Management/managementobjectcollection.cs

    还有来自 Microsoft 符号服务器的符号

    http://msdl.microsoft.com/download/symbols

    暗示 ManagementObjectCollection 是 IDisposable,这意味着它出于某种原因使用了非托管资源,或者它错误地使用了 IDisposable 接口。

    【讨论】:

      【解决方案4】:

      如果您执行大量 WMI 查询,则应始终处理返回 ManagementObjectCollection 以避免配额违规异常。

      System.Management.ManagementException: Quota violation
      at System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode)
      at System.Management.ManagementObjectCollection.ManagementObjectEnumerator.MoveNext()
      

      【讨论】:

        猜你喜欢
        • 2019-05-20
        • 2011-03-05
        • 1970-01-01
        • 2016-11-26
        • 1970-01-01
        • 1970-01-01
        • 2011-03-24
        • 2022-08-13
        • 2016-08-20
        相关资源
        最近更新 更多