1

我有几个用于与 c++ dll 通信的非托管内存结构。每个这样的结构都必须手动释放,所以我将它包装在MyUnmanagedStructurewhich implementsIDisposable中。

我总是需要可变数量的这些结构,所以我有一个MyUnmanagedStructureCollection也实现 IDisposable 的集合。

(请参阅下面的最小示例代码)

只要我的图书馆的用户总是调用 Dispose() 或包装集合using() {}没有问题,但我不能保证。即使用户不手动处理集合,我也不想泄漏内存。

MyUnmanagedStructureCollection.Dispose()垃圾收集通过终结器调用该方法时,据我所知,我不能确定我private List<MyUnmanagedStructure>的垃圾还没有被垃圾收集,那么在这种情况下如何处理每个结构?

在我的最终代码中,我是否应该尝试遍历列表,希望它还没有被垃圾收集?

在 try/catch 块中执行此操作并捕获 ObjectDisposedException 是否是一种好习惯?

或者我应该让每个非托管结构“自生自灭”,依靠各个终结器,而在我的集合的终结器中什么都不做?

public class MyUnmanagedStructureCollection : IDisposable
{
    private List<MyUnmanagedStructure> structures;
    private bool disposed = false;

    #region standard IDIsposable pattern
    public ~MyUnmanagedStructureCollection()
    {
        this.Dispose(false);
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            // Dispose unmanaged resources 
            // Should not access managed resources, 
            // the garbage collection may have claimed them already!

            // PROBLEM!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // this.structures is a List<MyUnmanagedStructure>; so is a managed resource!!!

            foreach (var structure in this.structures)
                 structure.Dispose(disposing)
            this.removeAllMemoryPressure();

            if (disposing) 
            {
                // Dispose managed resources.
                this.structures.Clear();
                this.structures = null;
            }

        }
        disposed = true;
    }
}


public class MyUnmanagedBuffer : IDisposable
{
...
}
4

2 回答 2

2

GC 的工作方式是:

  1. 查找所有可达对象
  2. 使用终结器将所有无法访问的对象排入终结队列
  3. 将所有可从终结队列中到达的对象标记为可到达
  4. 释放剩余的不可达对象

从正在运行终结器的对象引用的对象还不能被垃圾回收。

唯一需要注意的是终结的顺序是未定义的。因此,列表中的元素可能尚未最终确定,但尚未收集。完成保证是单线程的,所以你也需要锁定。

人们通常会试图避免这种终结链,因为独立终结更简单。但是,如果某些对象需要在其他对象之前被处理,那么这样的构造是不可避免的。

您还应该考虑使用SafeHandles.


可达性

完成的准则之一是 Finalize 方法不应触及其他对象。人们有时会错误地认为这是因为那些其他对象已被收集。然而,正如我已经解释过的,来自可终结对象的整个可达图都被提升了。

该指南的真正原因是避免接触可能已经完成的对象。那是因为最终确定是无序的。

克里斯布鲁姆谈定稿

于 2012-08-27T13:11:19.167 回答
1

由于从MyUnmanagedBuffer班级的角度来看,您的班级是托管资源MyUnmanagedStructureCollection,因此我认为它应该这样处理。这意味着该MyUnmanagedStructureCollection.Dispose(bool)方法如下。

protected virtual void Dispose(bool disposing) {
    if (!disposed) {
        // Dispose unmanaged resources 
        // Should not access managed resources, 
        // the garbage collection may have claimed them already!

        if (disposing) {
            // Dispose managed resources.
            // This means that we try to dispose all items in the structures collection.
            if (this.structures != null) {
                foreach (var structure in this.structures) {
                    structure.Dispose(disposing);
                    this.removeAllMemoryPressure(); // What does this?
                }
                this.structures.Clear();
                this.structures = null;
            }
        }
    }

    disposed = true;
}
于 2012-08-27T13:25:12.233 回答