机器视觉_HALCON_内存管理_C#中HObject对象申请释放解析


一、前言

使用HALCON联合C#进行开发时,若用HDevelop导出代码,可以在导出的代码文件中看到类似下面的代码,

    // Local iconic variables 
    HObject ho_Image=null;
	// Initialize local and output iconic variables 
    HOperatorSet.GenEmptyObj(out ho_Image);
    ...
    ho_Image.Dispose();
    HOperatorSet.GrabImageAsync(out ho_Image, hv_AcqHandle, -1);

从用户手册中可以知道,
在这里插入图片描述
ho_为前缀的ho_Image是一个图像变量(当然,根本原因是HObject类是图像类)。
在ho_Image从图像设备中获取图像前,还有一行代码ho_Image.Dispose();在这里插入图片描述
加这行代码的理由如上图所述,导出的C#代码中图像对象由HObject表示,但是从GC的角度看,HObject是非常小的,因此可能会被忽略。但HObject的底层可能是相当大的一块内存,为了避免泄露,所以用之前要显式释放(即使用Dispose())。

那么问题来了,为什么从GC的角度看,HObject不会被回收呢?为什么那么小的HObject底层可能是一块相当大的内存呢?为什么内存申请用的是GenEmptyObj()呢,用new HObject不行吗?

本文打算从VS中反编译的结果来分析这几个问题。


二、分析

1 GenEmptyObj()

其实有过一定开发经验和C语言基础的伙伴应该会有种感觉,它底层肯定是做了内存申请。
那到底是不是这样呢?我们往函数里跟两步,

可以看到底层的对象是通过new HObject()得到的,只不过构造函数不同,并且在new之前进行了一些额外处理(一个错误码的获取)。并且在new之后,从更底层的一个proc指针处加载图像(这部分应该是HALCON内的管理机制了)。所以GenEmptyObj本质上还是new了一个对象,不过做了一些判断与管理。

2 Dispose()

接下来看Dispose(),

可以看到,它的作用是让GC不要清理该对象。并保活(引用指定的对象,从当前例程的开始到调用此方法的点,使其不会被GC回收)。

所以Dispose()压根没有进行内存清理?
并不是的,第一行有一个key的判断,
在这里插入图片描述
从前面GenEmptyObj的反编译可知,底层new出来的对象,key默认是UNDEF(继续追踪下去会会发现是表示Zero)。
在这里插入图片描述

接着找到GrabImage函数,反编译得到,
在这里插入图片描述
可以看到,内部也有HObject.LoadNew函数,且内容与GenEmptyObj底层的LoadNew一致,即先new一个对象,然后从HALCON底层读取图像数据,
在这里插入图片描述
可以大胆猜测,此时的Key发生了变化。
因此Dispose()函数之初的Key值判断,很可能为判断该HObject的Key是否确实有指向底层的图像数据,若非UNDEF(Zero),则清除。

3 能否直接new对象?

如果你看过HDevelop示例目录下的WPF模板,会发现它的图像对象生成与释放可不是用GenEmptyObj和Dispose的,而是下面这样:

HImage image = new HImage("monkey");
HRegion region;
region = image.Threshold(128, 255);

除了对象是直接new出来的之外,似乎也没有释放的代码了。
对此,在 快速向导(Quick Guide) 文档中有说明,
在这里插入图片描述
简单讲,HALCON提供了两种写法,前者(HOperatorSet.GenEmptyObj())是过程式(面向过程)的写法;而后者(直接new的)是面向对象的写法。

那为什么面向对象的写法,即直接new也是可用的呢?继续反编译,
在这里插入图片描述
可以看到,new执行的构造函数本质上是调用了基类的有参构造函数,并且带有默认参数。追溯GenEmptyObj的底层代码可以发现,其底层new对象的参数列表和直接new的参数列表是一样的。因此,两者等价。

至于为什么没有用Dispose(),我觉得仅仅是因为这个示例没有用罢了。从反编译的结果来看,在每次使用前还是要用的。


三、结语

经过反编译解析,对HALCON图像对象内存的申请与释放有更深层次的认识,虽然不见得对实际开发有显著的帮助,但是用起来心里更有底。使用时,还是建议严格按照HALCON文档中要求的来,尽量风格统一。

还有一些注意事项:

  • 图像对象的直接赋值是引用赋值,如
     ho_Img = ho_ImgSrc;
     ho_Img.Dispose();
    
    这样的代码是有潜在风险的,因为ho_ImgSrc也被释放了,而你可能未察觉到。

猜你喜欢

转载自blog.csdn.net/BadAyase/article/details/130020059
今日推荐