此代码会导致托管堆损坏吗?
我正在尝试调试垃圾收集期间应用程序中发生的崩溃,并查看代码,我发现了两段相关的代码,如果不是问题的原因,至少对我来说是可疑的:
[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 未声明为值类型)。
正如我所说,我们遇到的崩溃通常发生在垃圾收集期间,但有时极难重现。我在另一个问题中提供了有关崩溃本身的更多详细信息,但我想要什么这里问的是:
- 上面的代码安全吗?
- 如果不是,它是否会成为托管堆损坏的原因并因此解释我们正在经历的崩溃?
编辑:我可能应该问一下,我在代码中发现的问题(因为本机代码和托管代码之间的结构大小不匹配)是否绝对肯定可能是GC 崩溃的原因。询问的原因是 i) C# 编译器不会抱怨错误的结构大小,并且 ii) 该问题很难重现。我现在很难让它在“旧”版本中崩溃(其中结构的大小是错误的),并且我想避免遵循可能的死胡同,因为每次测试可能需要很多天。
I'm trying to debug a crash that happens in our application during garbage collection and looking at the code I found two related pieces of code that, if not the cause of the problem, are at least suspicious to me:
[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;
}
Note that from my understanding the struct is actually at least 98 bytes in size but is declared as 96 bytes long (the code compiles though).
The second suspicious piece of code is related to the above struct:
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();
}
where m_RawData is a simple unsigned byte array and TMilbusData is the C++ (native) code analogous of the above struct, defined as
typedef struct
{
__int64 TimeStamp;
short Lane;
short TerminalAddress;
short TerminalSubAddress;
short Direction;
__int64 ErrorCounter;
__int64 MessageCounter;
short RTErrorState;
unsigned char Data[64];
} TMilbusData;
What I'm not sure about in this second case is if the conversion from the native struct to a managed reference type is safe (note that MilbusData is not declared as a value type).
As I said the crashes that we are experiencing occur normaly during garbage collection but are sometimes extremely difficult to reproduce. I gave more details about the crash itself in another question but what I want to ask here is:
- Is the code above safe?
- And if not can it be the cause for a managed heap corruption and explain therefore the crashes that we are experiencing?
EDIT: I should probably have asked if it is absolutely positive that the issues that I found in the code (as the mismatching structure sizes between native and managed code) can be the cause for a crash in the GC. Reason for asking is that i) The C# compiler doesn't complain about the wrong structure size and ii) The problem is very difficult to reproduce. I'm right now having a hard time in making it crash in the "old" version (where the size of the struct is wrong) and I wanted to avoid following a possible dead-end since each testing can take many days..
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您可能只想存储整数 32 位中的一定量的数据,例如前 16 位。
通过说结构的大小限制为 96 字节,如果您尝试将超过 96 字节放入结构中,您将尝试超出根据结构的大小分配的内存。
这意味着您将 1) 在结构中仅保留 96 个字节 2) 当您尝试放置比分配的内存更多的字节时,会遇到内存管理问题。
正如我已经说过的,你的代码没有任何问题,它会编译。在这种情况下它根本不正确,并且结构未正确声明,因此您应该不声明大小或声明正确的大小。
我只能向你保证结构大小不正确,为 96 字节,我无法告诉你连接到垃圾收集器的崩溃问题是否是连接到这个结构。如果这个结构是错误的,还有哪些其他结构是错误的?
我会修复大小,并确保数据的类型正确,以匹配您将从设备收到的数据。
You might only want to store a certain amount of the data, say the first 16 bits, of the 32 bits of an integer.
By saying the size of the structure is limited to 96 bytes, if you attempt to place more then 96 bytes into the structure, you will attempt to go outside of the memory you allocated based on the size of the structure.
This means you will 1) Only hold 96 bytes in the structure 2) When you attempt to place more then the allocated memory run into memory mangement problems.
As I have already said, there is nothing wrong with your code, it will compile. its simply not correct in this instance, and the structure is not declared correctly, so you should either not declare the size or declare the correct size.
All I can promise you is the structure size is not correct at 96 bytes, I cannot tell you if the problem you have with crash connected to the garbage collector, is connect to this structure. If this structure is wrong what other structures are wrong?
I would fix the size, and make sure Data is the correct type, to match the data you will recieve from your device.