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個通道較長時間穩定運行。

結果--我很開心,呵呵,也學到了很多原來沒有關注的知識和禁忌。

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