C#和halcon的混编程序出现序内存泄露、句柄持续增加、视觉程序运行越来越慢等问题的处理

这个话题非常重要。勇哥苦于手中的项目遇到这样的问题。

这些问题表现是:

(1)视觉程序内存占用会越来越大。这种内存变大通常是缓慢变大,几天不关机持续运行的话,大到几个G也不稀罕。通常,急速内存变大要容易找到问题些,最怕的是缓慢变大。

(2) 视觉程序会越运行越慢,会影响设备的PPM。通常工厂里的电脑一辈子不会关机,你可别指望他们会每班给你重启软件一次。

(3)程序句柄越来越多,大至几万都有可能。这是一种什么概念呢?你的windows启动后,会发现所有程序的句柄都不会超过6000个,而你的程序就有几万个!

(4)程序的CPU时间高。这种情况下设备运行也会越来越慢。有些程序甚至占用CPU时间跟北京时间走得一样准。这是一种什么概念? 你打开一个记事本,敲几个小时的字,CPU占用时间可能才几秒。


上述问题堪称自动化行业内的常见病。老板和工程师都为它头痛。


先来介绍几个重要的系统进程相关参数,它们分别是:

  • CPU时间

  • 线程

  • 句柄

  • 内存

  • 页面错误


在window10的任务管理器中,它们是通“详细信息”页面的选择列来调出的。见下图。

image.png


image.png


勇哥就用手中的一个项目,边学习边记录处理这类问题的心得。


这个程序运行后,CPU占有率25%,这个对于四核的Cpu来说,这相当于一个cpu已经被完全占用了。

另一个参数CPU时间,看上去跟北京时间还快。


image.png


经检查,下面这几段代码有问题

image.png


image.png

修改成下面这个样子。

image.png



image.png

现在CPU占有率正常了, cpu时间也正常了(仅仅是在程序启动后,不运行生产逻辑的阶段cpu时间正常了。但是本程序运行带视觉的生产逻辑后还是会有cpu占用时间多的问题)。

image.png

通常cpu占有率高这个比较容易处理,一般是While循环中没有考虑线程睡眠,释放cpu占用。

cpu时间这个是不太好处理的,在这里只是处理程序启动后不运行生产逻辑时的cpu时间高这个问题。


接下来研究一下比较艰难的部分,就是句柄与内存占用持续上升。


勇哥这个项止是基于halcon视觉的,存在每拍照处理一次内存会飙升的问题。另外一个是句柄数也会持续上对于句柄的概念,勇哥认为是win32编程里的一个概念,在win32 api里面,许多api都handle这个参数,可以理解句柄是WONDOWS用来标识被应用程序所建立或使用的对象的唯一整数。

如果句柄越来越高,则可以推测程序存在对象一直被创建却没有及时释放的隐患。


下图是勇哥启动程序时的内存与句柄数量,初始值,方便和后面的对照。

image.png


经过一次视觉拍照处理后,变成下面这么多

内存增加了88M,句柄增加了35个。

这个也不奇怪,halcon视觉第一次处理的时候,耗的内存会比较大,之后就消耗较小了。

image.png

真正想看到内存泄露需要长时观察才能确定。

下图是勇哥运行3000次视觉处理后的数据,可以看到内存达到589M,句柄数达到6805,可以肯定的是有内存泄露。

如果持续观察内存的占用,会发现一段时间内存会变小,也就是说有内存回收的动作。但是句柄数不会变小,会持续增加。这样虽然有了内存回收的动作,但是因为对象在不停止的增加,造成内存仍然在持续增加。

image.png


勇哥谈一下另一个任务管理器项目的概念,它就是“页面错误”

”页面错误“是进程中当数据不在内存而必须从磁盘检索的次数。

在windows里,有一个称为虚拟内存的机制,当物理可用内存不充足时,会把程序暂时用不到的数据先缓存到虚拟存中。这样在只有1G内存的系统中,所有运行着的程序他们提交的内存需求实际上可以超过1G的物理内存的。

不管你机器物理内存有多大,系统默认总会开启虚拟内存机制。勇哥的个人工作站服务器32G内存,照样默认开启了虚拟内存。 

如下图所示,虚拟内存一般是系统自动管理,当然你也可以自己管理,这时候选择自定义大小,一般来说可以把最大值最小值都设置为物理内存的两倍。

”无分页文件“指的是不启用虚拟内存,但是你的内存应该足够大才可以,否则后果自负。(之所以叫分页文件,是因为虚拟内存本身是在C盘下以一个隐藏属性的的文件方式存在的)


image.png

那么系统是怎么判断什么数据应该是放在虚拟内存中的,什么是属于急用的必须放物理内存的?

勇哥不知道,但我知道如果系统的可用内存不富裕时,许多数据会直接放到虚拟内存。

对于系统的可用内存这个参数许多人其实理解是不对的,如下图所示的,左边的”Windows任务管理器“中的空闲内存才是”可用内存“。

或者在”资源 管理器“中的可用内存,才是真正意义上的可以使用的物理内存。

而”Windows任务管理器“中的可用内存,指的是程序可以分配的包括虚拟内存和物理内存在内的总和。并非单指可用的物理内存。

image.png

我们再来看看“页面错误”,它其实就是说系统要读取的数据当前不在物理内存,而必须从虚拟内存载入,这时候就会报这个错误。

image.png

这个错误并非是指我们的软件有问题,而是我们的程序提交的内存较大,而物理内存不富裕,只能先放在虚拟内存。

页面错误很多,则表示磁盘交换的时间越多,则程序运行效率很降低。

而勇哥手中这个项目,是C# 加上halcon视觉的程序,占用内存比较大,并且又因为工业电脑只配置的4G内存,所有页面错误增量有几万次,非常大。

如下图所示:

image.png

但是经勇哥的研究,页面错误即使很多,也不是造成句柄不停的增加的原因。它只能反映出工业电脑搞视觉处理4G内存貌似不够用。

但是勇哥却发现可用内存很快耗光跟”页面错误“有关系,如果数量很大,你会发现可用内存速度耗光。

因此这里得到的一个经验是,判断内存泄露不能光看”内存(活动工作集)“,而是结合看看”可用内存“。因为”内存(活动工作集)“的值 可能没怎么改变,但是”可用内存“却很快耗光,它虽然主要是因为物理内存不足,同时也可能是内存泄露的原因。


接下来排除C#  混编halcon代码时可能的内存泄漏的地方

在混编时,最重要的一个原则见下面的说明:

摘自halcon文档:

The .net philosophy is to let the garbage collector remove unused objes. However,
 because the garbage collector detects unused objects only from time to time, 
 the used memory increases in the meantime. Even more important is that, 
 to the garbage collector, Halcon's iconic variables(image,regions,...) 
 seem to be rather "small",because they only contains a reference to the (in many cases rather large) iconic objects in the database. Thus ,
 the garbage collector may free such variables even if they are not used anymore.

 Therefor, you need to force the removal of (unused) objects. There are two ways to do this:

1) Call the garbage collector mannually . In the example Matching ,this is done after each processing run in the timer event:

private void Timer_Tick(object sender, System.EventArgs e)

                {

Action();

GC.Collect();

GC.WaitForPendingFinalizers():

}

C++ applications

GC::Collect():

GC::WaitForPendingFinalizers();

2) Dispose of individual objects manually by calling the method Dispose:

HImage  Image = new HImage("fuse");

...

Image.Dispose():

总结成一句话就是: 对象在使用前后都需要Dispose()

更准确的说是HObject 这样的变量,使用前后都要Dispose()。

另外对于局部与全部的HObject对象使用上有点区别:

对于局部的HObject对象只要注意在使用前后都要Dispose()即可。

但是对于全局的HObject对象,则可以参考下面的处理方式。

  HObject liveImage = null;
 
  HObject ExpGet_liveImage()
  {
     return liveImage;
  }
 
  void ExpSet_liveImage(HObject obj)
  {
      if (liveImage != null)
      liveImage.Dispose();
      liveImage = obj.CopyObj(1, -1); //如果直接liveImage=obj是不可以的,因为当Dispose后全局变量变量也会被注销。
  }


另外一种可能的情况是忘记关闭句柄。

例如下面的测量和OCV识别的算子用完后需要用下面的算子关闭句柄。如果忘记关,会发现每处理一张图片,内存指数上升的壮观现象。

close_measure (MeasureHandle)
clear_ocr_class_mlp(OCRHandle)

另外提一下,其实要避免C# 与halcon混编出现的内存泄露问题有一种办法是: 使用halcon引擎

即用halcon引擎直接运行halcon代码,不用导出来C#代码。(有空勇哥会写个专题讨论一下这种方式)

这样的好处即可以避免内存泄露的问题,还可以修改halcon代码不用重新编译C# 程序。

可惜勇哥手里的项目已经写成了C# 与halcon代码混编的模式了,推倒重来是不可能的。

勇哥2019/11/5注:

关于halcon引擎,见下面的贴子

C#中调用halcon引擎来执行hdev程序


对于网上的一段回收内存的代码:

#region 内存回收  
[DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
/// <summary>  
/// 释放内存  
/// </summary>  
public static void ClearMemory()  
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
}
}
#endregion

这是比较荒唐的代码,勇哥在halcon每次拍图前都调用它,会发现原来100多M的内存占用变成几M,其实是因为那个windows api函数:

SetProcessWorkingSetSize

它把原本在物理内存中的数据强制放到虚拟内存中去了。这只是忽哟用户的办法。



正当勇哥把上面一切手段都用过但是仍然找不到句柄增加的原因,一愁莫展时,这几天突然发现有一台机器上跑的程序句柄没有增加,它跑了两天都很稳定。

于是我对比了两者的操作系统版本、上位机软件的版本、内存、相机品牌、相机参数、曝光值、相机的网卡驱动,相机的静态IP设置(防止是因为IP冲突的原因),操,全部一样。

但是,好的那台单独拍照时间是180毫秒,其它有问题的机器是480毫秒。

这是什么鬼呢?


勇哥经反复实验,好机台上的相机的网络属性中,有一项是”MVTec GigE Vision Streaming Filter“

直接读意思应该是 MVtec的GigE视觉流过滤器。

这一项在其它问题机台上是不存在的。

但是如果取消勾选,会发现相机的拍照速度由原来180毫秒变成了480毫秒。证明此选项对拍照速度影响巨大!

image.png

这个东东是哪里来的呢,勇哥查了下资料,原来在halcon17安装的时候,如下图这项,默认是没有勾选的,你必做把它勾选。

它对采用GigE接口的相机有加速度的作用。

其实,下面窗口上说的很明白:如果你在halcon中用GigE接口的相机采图,记得勾选下面的选项。

image.png

但是对于非GigE接口的相机来说,如果多选了这些过滤器,反而可能造成拍照超时或者很慢的现象。关于这一点,请参考勇哥之前写的一个贴子:

BFLY-PGE-50A2M-CS相机(灰点)的设置经验


当这个拍照速度慢的问题解决后,程序的句柄就比较稳定了,不再持续增加了。

并且还带来了一个意想不到的好处: cpu占用时间也降下去了!

但是为什么拍照慢会造成句柄持续增加,以及会造成Cpu占用时间很高呢? 这个勇哥无法解释。


其实,折腾了许久,发现并不是程序出了什么问题,而是在硬件设置上出了问题。

这次经验也纠正了勇哥的一个错误观念:所有内存和句柄持续增加的问题都是程序有bug。其实也有可能是硬件的设置有问题! 


勇哥2020/10/28追加:

一、VS下不易发现的内存泄露

       在VS下调用Halcon算子时,要特别注意:所有输出的变量,不管有没有用到,都不要用NULL来替代。否则会导致程序会有2-4kb的内存泄露。(Halcon12及以前版本均存在这个问题,最新的版本是否存在有待验证)

  例如,我们需要计算某个区域的面积,而不关心另两个一并计算得出的中心点坐标,可能会这样写:

HTuple  hv_Area;AreaCenter(ho_Region, &hv_Area, NULL, NULL);

 这种方式会导致程序会有2-4kb的内存泄露,是不可取的,正确的方式如下:

HTuple  hv_Area,hv_Row,hv_Column;AreaCenter(ho_Region, &hv_Area, &hv_Row, &hv_Column);

 二、关于模板匹配的分值设置

       举个例子,在用find_shape_model寻找定位核时,设置分值是0.5,没有找到该定位核。降低该分值(如设成0.4),找到了,但是对应的匹配分值是大于0.5的?(Halcon17.12依然存在)

       也许是halcon自身的bug,也许是计算匹配分值的逻辑不太一样。目前做法是:把分值设置的低一些,找到后再按照偏高点的分值进行过滤

转载于:https://www.cnblogs.com/baiyy-daheng/p/8931467.html

三、halcon的异常官方解释

C:/Program Files/MVTec/HALCON-12.0/doc/html/manuals/extension_package_programmers_manual/extension_package_programmers_manual_0149.html

或者帮助文档,搜索“HALCON Error Codes”


--------------------- 

作者:hackpig
来源:
www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!




本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年4月    »
1234567
891011121314
15161718192021
22232425262728
2930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864