trivial destructor

項目中有一些地方爲了得到較快的速度,使用了無需釋放的簡易內存分配器:先一次分配一大塊內存,然後每次需要內存的時候從這塊內存裏面直接遞增分配合適大小的內存塊。當使用完這些內存了以後,不做顯式的釋放,直到整個大任務結束了以後,才一次釋放那整塊內存。這樣做優點在於

  • 內存分配非常快速,僅僅遞增指針即可
  • 沒有釋放的開銷(除了整塊釋放)
  • 不會有碎片

這種做法是很多引擎的常見選擇。但是也有一個很大的缺陷,就是每個分配對象的析構函數並沒有被調用。當然我們在釋放所有內存的時候也可以試圖調用所有的對象的析構函數,不過出於效率考慮,很多引擎並沒有在所有的模塊裏進行析構函數的調用,尤其是一些POD(plain old data),不調用析構函數也明顯無害。當然,這樣就留下了隱患。

根據墨菲定理,不想發生的事情,最終總會發生。最近項目就遇到了這樣的情況。

先是大家發現有很快速的內存泄漏,不巧的是,正好引擎的內存監測系統年久失修,本來可以秒殺的問題,由於內存監測系統的罷工,導致無法被快速定位。

手工人力定位,最後發現,問題就出在我們省略的析構函數調用上。

原來,新來的同事擴展了原來的POD結構,在POD裏面加上了資源分配,現在這就不是POD了,變成標準的類,順理成章的,他也添加了析構函數,在裏面釋放新分配出來的資源。問題就在於,這個類型是用前述的快速分配系統創建出來的,析構函數從我們設計之初就沒打算調用,這樣分配的資源無法釋放,造成了泄漏。

對於這樣有隱患的系統,的確無法繼續容忍,但我們也不打算爲這樣的例外犧牲效率,對所有快速分配系統調用析構函數。所以問題的核心,就變成了我們是否能夠檢測的這種情況了。

解決問題的想法是,我們能否檢測到這個情況,如果能在編譯時刻檢測到這一情況,我們就可以報錯,或者用模版來產生不同的代碼,從而對POD沿用舊的機制,沒有額外的開銷,其他類則自動調用析構函數。

進一步簡化問題,我們如果能確定一個類有non-trivial destructor,我們就要在釋放的時候對這些對象調用析構函數。

google了一下,原來stl裏面就有一個std::has_trivial_destructor,大喜,使用,果然有用,用這個模板類可以檢測出一個類有沒有non-trivial destructor。這樣就很容易能實現編譯時刻的類型檢測,從而產生報警或是模版類處理析構情況。

由於項目中使用的是eastl,就原樣替換成eastl的has_trivial_destructor,發現失敗了。。。原來eastl只支持檢測一個類型是否是pod,但是這個pod檢測也不準。。。放了一個pod的struct,居然說不是pod。看了eastl代碼,發現注視裏面提到,考慮到跨平臺的問題,說只能做到那樣了。。。但是vc自帶stl是可以工作的,再去看VC stl實現代碼,vc的stl可讀性太差了。。。都是各種宏替換。遂使用編譯器預處理cpp文件,產生了一個.i文件,看了一下,裏面檢測non-trivial destructor的地方調用了一個__has_trivial_destructor。這個東西有意思,據我所知一般vc裏面__開頭的函數都是intrinsic函數。查了msdn,真是vc自己擴展的,還順藤摸瓜,查到了其他一堆檢測c++類型的intrinsic函數。

問題迎刃而解,只要用vc的擴展函數就可以搞定了。但是這東西犧牲了跨平臺性,也算雙刃劍。


http://www.cnblogs.com/gamedev/archive/2012/08/10/2631255.html

發佈了398 篇原創文章 · 獲贊 71 · 訪問量 63萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章