2011-09-28 72 views
3

我們有一個用Visual Basic 6.0編寫的前端,它調用混合C/C++編寫的幾個後端DLL。問題是每個DLL似乎都有自己的堆,其中一個不夠大。當我們分配了足夠的內存時,堆會與程序堆棧發生衝突。 除了用C++編寫的基本DLL封裝外,每個DLL都完全用C語言編寫。每個DLL都有一些入口點。每個入口點立即調用一個C例程。我們希望增加DLL中堆的大小,但一直未能弄清楚如何做到這一點。我搜索了指導,並發現這些MSDN文章:如何在混合語言應用程序中創建堆?

http://msdn.microsoft.com/en-us/library/hh405351(v=VS.85).aspx

這些文章很有意思,但提供了矛盾的信息。在我們的問題中,似乎每個DLL都有自己的堆。這與「堆:樂趣和痛苦」文章相匹配,文章說C啓動時(C RT)庫在啓動時創建了自己的堆。 「管理堆內存」文章指出,C RT庫分配在默認進程堆外。 「Win32中的內存管理選項」文章說這種行爲取決於正在使用的C RT庫的版本。

我們通過從私有堆中分配內存來暫時解決了這個問題。但是,爲了改進這個非常大型的複雜程序的結構,我們希望從一個C++包裝器切換到真正的具有類的C++。我們非常肯定,新的自由操作符不會從我們的私有堆中分配內存,我們想知道如何控制C++在每個DLL中分配對象時使用的堆的大小。該應用程序需要從2000年到7

問題在桌面Windows-NT的所有版本中運行,

任何人都可以帶我們去明確和正確的文件是 講解了如何控制規模堆C++用來分配 對象?

有幾個人聲稱由堆分配覆蓋堆棧造成的堆棧損壞是不可能的。這是我們觀察到的。 VB前端使用它動態加載的四個DLL。每個DLL獨立於其他DLL並提供前端調用的一些方法。所有的DLL通過寫入磁盤文件的數據結構進行交流。這些數據結構都是靜態構造的。它們不包含指針,只是值類型和固定大小的值類型數組。問題DLL由文件名傳遞的單個調用調用。它旨在分配完成處理所需的大約20MB數據結構。它做了很多計算,將結果寫入磁盤,釋放20MB的數據結構,並返回和錯誤代碼。然後前端卸載DLL。在調試討論中的問題時,我們在數據結構分配代碼的開頭設置了一個斷點,並觀察calloc調用返回的內存值,並將它們與當前的堆棧指針進行比較。我們看着分配的塊接近堆棧。分配完成後,堆棧開始增長,直到堆棧重疊。最終計算結果寫入了堆並損壞了堆棧。隨着堆棧解開,它試圖返回到一個無效地址並且因分段錯誤而崩潰。

我們的每個DLL都靜態鏈接到CRT,這樣每個DLL都有自己的CRT堆和堆管理器。微軟在http://msdn.microsoft.com/en-us/library/ms235460(v=vs.80).aspx中說:

CRT庫的每個副本都有一個單獨的不同的狀態。 因此,諸如文件句柄,環境變量和 語言環境的CRT對象僅對分配或設置了這些對象的CRT副本有效。當DLL及其用戶使用CRT庫的不同副本時,無法將這些CRT對象跨越DLL邊界 傳遞,並期望它們在另一端正確拾取。
另外,因爲CRT庫的每個副本都有自己的堆管理器,所以在一個CRT庫中分配內存並將指針穿過 DLL邊界,並由另一個CRT庫的副本釋放,這是 的潛在原因堆腐敗。

我們不通過DLL之間的指針。我們沒有遇到堆腐敗,我們遇到堆棧損壞。

+0

希望你得到一個正確的答案,但如果你不這樣做,你總是可以覆蓋'new'和'delete'運算符來處理一個私有堆。 –

+0

謝謝。我知道我們可以做到這一點,但這是一個相當複雜的解決方案。如果我們能夠計算出如何去做,那麼增加運行時提供的堆的大小就簡單多了。我們只使用私人堆,因爲我們還沒有弄清楚這一點。 –

+1

碰撞是不可能的。請澄清。 –

回答

2

OK,問題是:

任何人都可以帶我們去明確和正確的文件是 講解了如何控制++使用分配 對象堆C的大小?

我要回答我自己的問題。我從Raymond Chen的博客The Old New Thing,特別是There's also a large object heap for unmanaged code, but it's inside the regular heap得到了答案。在那篇文章中,雷蒙德建議Mario Hewardt和Daniel Pravat的Advanced Windows Debugging。這本書有非常具體的信息堆棧和堆腐敗,這是我想知道的。作爲一個優點,它提供了關於如何調試這些問題的各種信息。

1

能否請您闡述一下您的這個說法:

堆與程序堆棧碰撞時,我們已經分配足夠的存儲空間。

如果我們正在談論Windows(或任何其他成熟的平臺),這應該不會發生:操作系統確保堆棧,堆,映射文件和其他對象不會相交。

另外:

任何人都可以帶我們去明確和正確的文件,說明如何控制堆C的大小++用來分配對象?

在Windows上堆大小不固定:它隨着應用程序使用越來越多的內存而增長。它會增長,直到使用該進程的所有可用虛擬內存空間。確認這一點很容易:只需編寫一個簡單的測試應用程序,它可以持續分配內存並計算已分配的內存量。在默認的32位Windows上,你將達到將近2Gb。當然,最初堆不佔用所有可用空間,因此它必須在此過程中增長。

沒有很多關於「碰撞」的細節,很難說出你的情況發生了什麼。但是,查看這個問題的標籤會提示我一種可能性。這是可能的(不幸的是,經常發生)分配內存區域的所有權正在模塊之間傳遞(在你的情況下是DLL)。這裏的情景:

  • 有兩個DLL:A和B兩人都創造了他們自己的堆
  • DLL中的在其堆中分配一個對象,並將指針和所有權到B
  • 的DLL B接收該指針,採用存儲器和解除分配對象

如果堆是不同的,如果實際上被解除分配的存儲器區域屬於它(主要是出於性能原因)最堆管理器將不會檢查。所以他們會放棄一些不屬於他們的東西。通過這樣做,他們腐敗了其他模塊的堆。這可能(並且經常)導致崩潰。但不總是。根據您的運氣(以及特定的堆管理器實現),此操作可能會改變其中一個堆,方式是下一個分配將發生在堆所在的區域之外。

這經常發生在一個模塊是託管代碼時,而另一個模塊是本地模塊。既然你在問題中有VB6標籤,我會檢查是否是這種情況。

+0

您可以檢查一下,因爲我已經添加了更多的細節。 –

0

如果堆棧增長得足夠大以至於堆中堆棧溢出可能會成爲問題:傳入的無效數據不滿足某些遞歸的退出條件(循環檢測不工作或不存在)問題DLL使無限遞歸消耗可笑的大堆棧空間。人們會期望這樣一個DLL以堆棧溢出異常終止,但是對於編譯器/鏈接器優化或大型外部堆大小而言,它會在其他地方崩潰。

+0

我們沒有看到堆棧溢出。令人驚訝的是,這個應用程序的總分配空間總共很少超過40MB。我們一直假設VB6正在做一些優化來處理Win95內存管理問題,而現在不必要的優化正在給我們帶來麻煩。我準備對此有所錯誤。 –

+0

...在這種情況下,您不會鏈接到MSVCPxx,並且新運營商也會擁有其私有堆。缺點是STL/WTL/ATL中的每個模板類都將被編譯到C++ DLL中,從而使其體積更大。此外,如果您希望在您的C++ DLL中使用boost,則可能需要編譯該批以進行靜態鏈接。 – aquaherd

0

堆是由CRT創建的。也就是說,malloc堆是由CRT創建的,並且與HeapCreate()無關。但它並未用於大量分配,而是直接交給操作系統。

隨着多個DLL,你可能有多個堆(更新VC版本在共享更好,但即使VC6沒有問題,如果你使用MSVCRT.DLL - 這是共享)

堆棧,而另一方面,由OS管理。在這裏你可以看到爲什麼多堆不重要:不同堆的操作系統分配永遠不會與堆棧的操作系統分配衝突。

請注意,操作系統可能會在堆棧附近分配堆空間。該規則只是沒有重疊,畢竟沒有保證「未使用的分離區」。如果你有緩衝區溢出,它可能會溢出到堆棧空間。

那麼,任何解決方案?是的:轉到VC2010。它有緩衝區安全檢查,以相當有效的方式實施。即使在發佈模式下,它們也是默認設置。

相關問題