我試圖在垃圾收集期間調試應用程序中發生的崩潰,並查看代碼,發現兩個相關的代碼段,如果不是問題的原因,至少對我來說是可疑的:此代碼是否會導致託管堆損壞?
[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)問題非常難以重現。我現在很難讓它在「舊」版本中崩潰(結構的大小錯誤),並且我想避免發生可能的死角,因爲每次測試都需要很多天。
只需對代碼進行快速掃描,就可以確定您在第二塊代碼的if語句中重新定義了「ret」值。如果沒有其他可能會導致混淆。 – pstrjds
感謝您指出這一點,編輯代碼以將其發佈到此處時是錯誤的,現在已修復。 – floyd73
您的託管代碼中沒有創建足夠的空間。你的陣列也是不同的大小。你是否已經驗證了結構的當前規模,因爲根據你發佈的內容,它甚至不足以容納所有東西。這是什麼軍標,看起來幾乎像MIL-STD-1553。你是如何知道你的C++代碼的,通常我必須創建一個包裝類,如果我正在處理類似這樣的問題 –