2011-10-04 130 views
5

我試圖在垃圾收集期間調試應用程序中發生的崩潰,並查看代碼,發現兩個相關的代碼段,如果不是問題的原因,至少對我來說是可疑的:此代碼是否會導致託管堆損壞?

[StructLayout(LayoutKind.Sequential, Size = 96, CharSet = CharSet.Ansi, Pack=1)] 
public class MilbusData 
{ 
    public System.Int64 TimeStamp; 
    public System.Int16 Lane; 
    public System.Int16 TerminalAddress; 
    public System.Int16 TerminalSubAddress; 
    public System.Int16 Direction; 
    public System.Int64 ErrorCounter; 
    public System.Int64 MessageCounter; 
    public System.Int16 RTErrorState;  
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 
    public System.UInt16[] Data; 
} 

請注意,從我的理解,結構實際上至少有98個字節的大小,但被宣佈爲96個字節長(代碼編譯雖然)。

代碼的第二可疑片與上述有關的結構:

MilbusData^ ret = nullptr; 
if (m_Stream->Read(m_RawData, 0, sizeof(TMilbusData)) == sizeof(TMilbusData)) 
{ 
    GCHandle pinnedRawData = GCHandle::Alloc(m_RawData, GCHandleType::Pinned); 

    ret = (MilbusData^)Marshal::PtrToStructure(pinnedRawData.AddrOfPinnedObject(), 
              MilbusData::typeid); 

    pinnedRawData.Free(); 
} 

其中m_RawData是一個簡單的無符號字節數組和TMilbusData是C++(天然)上述結構的代碼類似,定義爲

typedef struct 
{ 
    __int64 TimeStamp; 
    short Lane; 
    short TerminalAddress; 
    short TerminalSubAddress; 
    short Direction; 
    __int64 ErrorCounter; 
    __int64 MessageCounter; 
    short RTErrorState; 
    unsigned char Data[64]; 
} TMilbusData; 

我不知道關於第二種情況是什麼,如果從本機結構的管理引用類型轉換是安全的(注意,MilbusData未聲明的值類型)。

正如我所說的,我們遇到的崩潰在垃圾收集期間正常發生,但有時非常難以重現。我在另一個question中給出了更多有關崩潰本身的細節,但我想問的是:

  • 上面的代碼是否安全?
  • 如果不是,它可以成爲一個託管堆腐敗的原因,並解釋我們正在經歷的崩潰?

編輯:我也許應該問,如果它是我在代碼中找到絕對肯定的問題(如原生之間的不匹配的結構尺寸和託管代碼)可以是一個崩潰的原因在GC中。詢問的理由是:i)C#編譯器不會抱怨錯誤的結構尺寸,並且ii)問題非常難以重現。我現在很難讓它在「舊」版本中崩潰(結構的大小錯誤),並且我想避免發生可能的死角,因爲每次測試都需要很多天。

+0

只需對代碼進行快速掃描,就可以確定您在第二塊代碼的if語句中重新定義了「ret」值。如果沒有其他可能會導致混淆。 – pstrjds

+0

感謝您指出這一點,編輯代碼以將其發佈到此處時是錯誤的,現在已修復。 – floyd73

+1

您的託管代碼中沒有創建足夠的空間。你的陣列也是不同的大小。你是否已經驗證了結構的當前規模,因爲根據你發佈的內容,它甚至不足以容納所有東西。這是什麼軍標,看起來幾乎像MIL-STD-1553。你是如何知道你的C++代碼的,通常我必須創建一個包裝類,如果我正在處理類似這樣的問題 –

回答

3

但是不應該編譯器看到96個字節不夠 存儲該結構並將其視爲錯誤?無論如何,單獨這個 是否可以解釋垃圾收集期間的崩潰?

您可能只想存儲一定數量的數據,比如32位整數的前16位。

通過說結構的大小限制爲96個字節,如果您嘗試將更多96個字節放入結構中,您將嘗試根據結構的大小超出您分配的內存。

這意味着您將1)僅在結構中保存96個字節2)當您嘗試放置更多分配的內存時,會遇到內存管理問題。

正如我已經說過,你的代碼沒有問題,它會編譯。它在這種情況下根本不正確,並且結構沒有正確聲明,因此您不應該聲明大小或聲明正確的大小。

編輯:我也許應該問,如果它是有絕對把握認爲 ,我在代碼中發現(爲錯配結構,原生之間 尺寸和託管代碼),這些問題可以在 崩潰的原因GC。詢問的理由是i)C#編譯器不會抱怨 關於錯誤的結構尺寸和ii)難以重現的問題是 。我現在很難在 「舊」版本(其中結構的大小錯誤)中崩潰,並且我希望 避免發生可能的死角,因爲每次測試可能需要多少天才能完成 ..

所有我能承諾的是,在96字節的結構大小不正確,我不能告訴你,如果你有與崩潰連接到垃圾回收器的問題,連接到這個結構。如果這個結構錯了,其他結構是錯的?

我會修正大小,並確保數據是正確的類型,以匹配您將從設備接收的數據。

+0

感謝Ramhound,正如我在我目前測試的評論中討論的那樣,如果應用程序崩潰時聲明的結構大小正確,但這可能需要很多天。我將在測試後發佈我的結果,並在此之前將問題留待。 – floyd73

+0

@ floyd73 - 在繼續測試之前,您應該驗證** m_RawData **是否是正確的大小。我不相信sizeof能夠確定你的數組的大小,如果它是空的。 –

+0

我剛剛讓舊的應用程序崩潰,現在將測試固定代碼,看看它是否在未來24小時內崩潰,謝謝你的幫助! (關於m_RawData它只被分配一次,所以它的大小是不變的,我在if子句中檢查的是從流中讀取的字節數) – floyd73