【发布时间】: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 似乎已经消失了。
最后,我想知道几件事:
- 如果我手动调用
Dispose(),是否有可能稍后也会调用析构函数? - 有没有可能
MyOwnImage的内部对象在这个类的析构函数被调用之前就被销毁了? - 如果有 OpenCV 专家:有没有可能处理 mat 不会释放里面的数据?如果是,是错误还是有原因?
【问题讨论】:
-
这里的所有权模型有缺陷。您有一个类在其构造函数中采用
Mat,该构造函数是在其他地方创建的,但随后负责释放它 - 这很容易出错。无论创建什么Mat都应该负责确保它被处理掉。在这里你没有那个保证。Mat是在哪里创建的?考虑在构造函数中克隆Mat- 这会创建一个新实例,然后您的类可以负责。它仍然没有解决如何处理您在此类之外创建的Mat的问题。 -
看来您可能正在“发明”disposable pattern(用于非托管资源)。该模式包括多次调用 Dispose 或根本不调用 Dispose 并且清理工作留给终结器的情况。