2006-06-29工作琐记,Eoutofmemory问题的解决历程

偷的浮生半日闲,再留个记号,呵呵。这两天最高兴的事就是困扰我半年之久的EOutOfMemory问题终于得到了一定程度的解决。

问题的 大致情况是这样的,我使用C++ Builder编写了一个通过串口发送短信彩信的程序,当然免不了是从单串口修改为多串口,在单串口情况下,我使用了一个动态创建的进度提示窗体。修改为多串口时候,“最简单”的方法当然是使用多线程,这样在单线程稳定的前提下,修改主要集中在线程的控制和资源访问临界处理上。想法没有问题,问题的起因在于测试中,设想的一个串口对应一个进度提示窗体这种方式经常造成程序停摆,当然大家都知道这种情况一般是VCL界面临界保护不当。后来解决这个问题之后,发现通道增加后,经常出现程序异常关闭退出的情况。这种问题是编程的大忌讳。根据分析,发现有两个问题:

1)创建线程失败

2)EOutOfMemory引起的局部变量申请内存为空

问题一,简单的理解是资源不足,或者句柄,或者内存,我得出的结论是线呈创建失败一般和堆空间不足有关

问题二,这个问题困扰我时间最长,可以说是因为原来对于win32 PE程序一些基本概念总是没有弄太清楚,也有部分原因归罪到我使用的BCB上。通过查找google,一直找不到合适的解决方法。最后受别人提出的一个问题启发,怀疑可能是BCB对于堆空间的管理有问题。即频繁的多线程内存申请、变更、释放造成堆空间碎片,从而造成内存申请失败(直到现在,其实也无法解决为什么在malloc(1)也会失败,此时碎片再多,也不能解释1字节无法申请的现象,我还是认为只是和BCB有关)。

WIN32程序的PE头中有4个定义,分别是max/min heap size和max/min stack size。简单的来讲我们常说堆栈,其实很多朋友可能没有注意到堆和栈的区别。编译器对于堆栈的使用一般是这样的:

1)栈空间一般用在函数参数和局部变量的使用,调用函数时分配堆栈,函数执行完毕回收,局部变量由编译器从栈空间中自动分配,例如 int,char[],AnsiString的指针本身都是占用了栈空间,其处理简单,不需要我们人工参与,缺点是栈空间一般有限,不能存放过多的局部变量。

2)堆空间一般用来临时变量的内存申请,例如new,malloc的操作都是针对程序的堆空间而言,早期的win32s还有local heap和global heap的区分,win2000以后已经不再区分了。

另外一个关键点就是我们的程序本身都会接受编译器提供的内存管理,BCB好像是一个MEMmanager类,代码中的malloc/realloc/free和new/delete操作被内存管理解析执行,那么不同的编译器对于内存的使用情况就会存在差异。

BCB对于内存的管理可能是着重于使用资源的效率和节省上,在高频度多线程频繁操作堆时存在一些不足,那就是堆碎片。

解决的方法很简单,牺牲空间。char pt =(char *)malloc(1); pt = (char *)realloc(2)的结果如何?跟踪看看,BCB对于两次操作分配的地址不同,如果减低分配的频度就可以减少堆碎片的产生。

经过这么处理之后,原来程序在启动16通道时无法保证不出现EOutOFMemory问题,现在已经可以同时启动99个通道较长时间稳定运行。

结果--我很开心,呵呵,也学到了很多原来没有关注的知识和禁忌。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章