这段代码会导致托管堆损坏吗?

本文关键字:损坏 段代码 代码 | 更新日期: 2023-09-27 18:10:35

我试图调试在垃圾收集期间发生在应用程序中的崩溃,并查看代码,我发现两个相关的代码片段,如果不是问题的原因,至少对我来说是可疑的:

[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没有声明为值类型)。

正如我所说,我们正在经历的崩溃通常发生在垃圾收集期间,但有时极其难以重现。我在另一个问题中给出了更多关于崩溃本身的细节,但我想问的是:

  • 以上代码安全吗?
  • 如果不是,它是否可以成为托管堆损坏的原因,并因此解释我们正在经历的崩溃?

EDIT:我可能应该问一下,我在代码中发现的问题(如本机代码和托管代码之间的结构大小不匹配)是否绝对是肯定的可能是导致GC崩溃的原因。问这个问题的原因是:1)c#编译器不会抱怨错误的结构大小,2)这个问题很难重现。我现在很难让它在"旧"版本中崩溃(结构体的大小是错误的),我想避免跟随可能的死胡同,因为每次测试可能需要许多天。

这段代码会导致托管堆损坏吗?

,但编译器不应该看到96字节不足以存储结构体并将其视为错误?在任何情况下,可以单独解释垃圾收集时的崩溃吗?

您可能只想存储一定数量的数据,例如32位整数的前16位。

通过说结构的大小被限制为96字节,如果您试图在结构中放置超过96字节,您将尝试超出基于结构大小分配的内存。

这意味着你将1)在结构中只保留96个字节2)当你试图放置超过分配的内存时,会遇到内存管理问题

正如我已经说过的,你的代码没有任何问题,它会编译。在这种情况下,它是不正确的,并且结构没有正确声明,所以你应该要么不声明大小,要么声明正确的大小。

编辑:我可能应该问一下这是绝对肯定的我在代码中发现的问题(如不匹配的结构)(本机代码和托管代码之间的大小)可能是导致崩溃的原因GC。问这个问题的原因是i) c#编译器不会抱怨2)这个问题很难解决复制。我现在很难把它装进去"旧"版本(结构的大小是错误的)和我想要的避免走到可能的死胡同,因为每次测试都需要很多天. .

所有我可以向你保证的是结构大小在96字节不正确,我不能告诉你如果你有崩溃连接到垃圾收集器的问题,是连接到这个结构。如果这个结构是错的其他结构是错的吗?

我会固定大小,并确保数据是正确的类型,以匹配您将从设备接收到的数据。