【问题标题】:Mat is not being destroyed properly垫子没有被正确销毁
【发布时间】:2021-09-30 13:23:57
【问题描述】:

在我用 C# 编写的软件中,我实现了一个用于内部目的的图像类。在那个班级里,我拿着来自 emgu CV 的 Mat

class MyOwnImage : IDisposable
{
    private Mat mat;

    public MyOwnImage( Mat mat ) {
        this.mat = mat;
    }

    public void Dispose() {
        mat.Dispose();
    }
    //...
}

现在,我发现,无论我是否在 Image 对象上调用 Dispose(),我的内存使用量都在增长。我仔细查看了Mat 类,发现它内部有IntPtr 形式的未管理内存。所以,我试了一下,如果手动释放指针会有所不同:

class MyOwnImage : IDisposable
{
    private Mat mat;

    public MyOwnImage( Mat mat ) {
        this.mat = mat;
    }

    public void Dispose() {
        Marshal.FreeHGlobal( mat.DataPointer ); // data pointer is the IntPtr
        mat.Dispose();
    }
    //...
}

我有点惊讶,但它解决了内存溢出的问题。所以,我假设我必须手动销毁Mat 对象中的数据。之后,如果有人忘记手动Dispose() 图像,我尝试通过添加析构函数来使其更加稳定。

class MyOwnImage : IDisposable
{
    private Mat mat;

    public MyOwnImage( Mat mat ) {
        this.mat = mat;
    }

    ~MyOwnImage() {
        destroy();
    }

    private void destroy() {
        Marshal.FreeHGlobal( mat.DataPointer ); // data pointer is the IntPtr
        mat.Dispose();
    }

    public void Dispose() {
        destroy();
    }
    //...
}

可悲的是,这变成了一个新问题:有时,当调用析构函数时,Mat 对象似乎已经被销毁了。随后,在调用FreeHGlobal(...) 的线路上抛出AccessViolationExcetion。现在,我进行了另一个测试,并删除了Dispose() 函数中的destroy() 调用。

class MyOwnImage : IDisposable
{
    private Mat mat;

    public MyOwnImage( Mat mat ) {
        this.mat = mat;
    }

    ~MyOwnImage() {
        destroy();
    }

    private void destroy() {
        Marshal.FreeHGlobal( mat.DataPointer ); // data pointer is the IntPtr
        mat.Dispose();
    }

    public void Dispose() {
        //destroy();
    }
    //...
}

奇怪的是,这并没有改变任何事实,当调用析构函数 ~MyOwnImage 时,MyOwnImage 中的 mat 似乎已经消失了。

最后,我想知道几件事:

  1. 如果我手动调用Dispose(),是否有可能稍后也会调用析构函数?
  2. 有没有可能MyOwnImage的内部对象在这个类的析构函数被调用之前就被销毁了?
  3. 如果有 OpenCV 专家:有没有可能处理 mat 不会释放里面的数据?如果是,是错误还是有原因?

【问题讨论】:

  • 这里的所有权模型有缺陷。您有一个类在其构造函数中采用Mat,该构造函数是在其他地方创建的,但随后负责释放它 - 这很容易出错。无论创建什么 Mat 都应该负责确保它被处理掉。在这里你没有那个保证。 Mat 是在哪里创建的?考虑在构造函数中克隆Mat - 这会创建一个新实例,然后您的类可以负责。它仍然没有解决如何处理您在此类之外创建的Mat 的问题。
  • 看来您可能正在“发明”disposable pattern(用于非托管资源)。该模式包括多次调用 Dispose 或根本不调用 Dispose 并且清理工作留给终结器的情况。

标签: c# emgucv


【解决方案1】:

J... 在 cmets 中指出了问题。

违反了所有权模型。正如他所建议的,我在 MyOwnImage 中克隆了 Mat 对象,现在它可以工作了。

class MyOwnImage : IDisposable
{
    private Mat mat;

    public MyOwnImage( Mat passedMat ) {
        var copy = new Mat();
        passedMat.CopyTo( copy );
        mat = copy;
    }

    public void Dispose() {
        mat.Dispose();
    }
    //...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多