我正在尝试调试垃圾收集过程中我们的应用程序中发生的崩溃并查看代码我发现了两个相关的代码片段,如果不是问题的原因,至少对我来说是可疑的:
[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;
}
请注意,根据我的理解,struct实际上至少有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未声明为值类型)。 / p>
正如我所说的那样,我们正在经历的崩溃在垃圾收集过程中正常发生,但有时很难再现。我在另一个question 中提供了关于崩溃本身的更多细节,但我想在这里问的是:
编辑:我可能会问我是否绝对肯定我在代码中发现的问题(作为本机代码和托管代码之间的不匹配结构大小)可能是GC崩溃的原因。问的原因是i)C#编译器没有抱怨错误的结构大小和ii)问题很难重现。我现在很难让它在“旧”版本中崩溃(结构的大小错误)并且我想避免跟随可能的死胡同,因为每次测试可能需要很多天.. < / p>
答案 0 :(得分:3)
但编译器不应该看到96个字节不够 存储结构并将其视为错误?无论如何,这可能 单独解释垃圾收集过程中的崩溃?
您可能只想存储一个32位整数的一定数量的数据,比如前16位。
通过说结构的大小限制为96个字节,如果你试图在结构中放置超过96个字节,你将尝试根据结构的大小超出你分配的内存。
这意味着您将1)仅在结构中保留96个字节2)当您尝试放置更多时,分配的内存会遇到内存管理问题。
正如我已经说过的,你的代码没有任何问题,它会编译。它在这种情况下根本不正确,并且结构未正确声明,因此您应该声明大小或声明正确的大小。
编辑:我应该问过这是否绝对是正面的 我在代码中发现的问题(作为不匹配的结构 本机代码和托管代码之间的大小)可能是导致崩溃的原因 GC。问的理由是i)C#编译器没有抱怨 关于错误的结构尺寸和ii)问题非常困难 重现。我现在很难让它崩溃 “旧”版本(结构的大小错误),我想要 因为每次测试都可以避免可能的死胡同 很多天..
所有我可以保证你的结构大小是不正确的96字节,我不能告诉你,如果崩溃连接到垃圾收集器的问题,是连接到这个结构。如果这种结构错误,那么其他结构是错误的吗?
我会修正尺寸,并确保数据是正确的类型,以匹配您将从设备接收的数据。