.net托管环境下struct实例字段的内存布局(Layout)和大小(Size)

简介:

 在C/C++中,struct类型中的成员的一旦声明,则实例中成员在内存中的布局(Layout)顺序就定下来了,即与成员声明的顺序相同,并且在默认情况下总是按照结构中占用空间最大的成员进行对齐(Align);当然我们也可以通过设置或编码来设置内存对齐的方式,有关C/C++中(设置)内存对齐的讨论,可以参考我以前写的一篇面试手记《总结面试时没有回答上的设置内存对齐方式问题》。

    然而在.net托管环境中,CLR提供了更自由的方式来控制struct中Layout:我们可以在定义struct时,在struct上运用StructLayoutAttribute特性来控制成员的内存布局。默认情况下,struct实例中的字段在栈上的布局(Layout)顺序与声明中的顺序相同,即在struct上运用[StructLayoutAttribute(LayoutKind.Sequential)]特性,这样做的原因是结构常用于和非托管代码交互的情形。如果我们正在创建一个与非托管代码没有任何互操作的struct类型,我们很可能希望改变C#编译器的这种默认规则,因此LayoutKind除了Sequential成员之外,还有两个成员Auto和Explicit,给StructLayoutAttribute传入LayoutKind.Auto可以让CLR按照自己选择的最优方式来排列实例中的字段;传入LayoutKind.Explicit可以使字段按照我们的在字段上设定的FieldOffset来更灵活的设置字段排序方式,但这种方式也挺危险的,如果设置错误后果将会比较严重。下面就看几个示例,算下四个struct各占多少Byte?

1.[StructLayout(LayoutKind.Sequential)]

struct  StructDeft // C#编译器会自动在上面运用[StructLayout(LayoutKind.Sequential)]
{
    
bool i;  //1Byte
    double c;//8byte
    bool b;  //1byte
}
    

    sizeof(StructDeft)得到的结果是24byte!啊哈,本身只有10byte的数据却占有了24byte的内存,这是因为默认(LayoutKind.Sequential)情况下,CLR对struct的Layout的处理方法与C/C++中默认的处理方式相同,即按照结构中占用空间最大的成员进行对齐(Align)。10byte的数据却占有了24byte,严重地浪费了内存,所以如果我们正在创建一个与非托管代码没有任何互操作的struct类型,最好还是不要使用默认的StructLayoutAttribute(LayoutKind.Sequential)特性。

 


2.[StructLayout(LayoutKind.Explicit)]

[StructLayout(LayoutKind.Explicit)]
struct  BadStruct
{
    [FieldOffset(
0)]
    
public bool i;  //1Byte
    [FieldOffset(0)]
    
public double c;//8byte
    [FieldOffset(0)]
    
public bool b;  //1byte
}

    sizeof(BadStruct)得到的结果是9byte,显然得出的基数9显示CLR并没对结构体进行任何内存对齐(Align);本身要占有10byte的数据却只占了9byte,显然有些数据被丢失了,这也正是我给struct取BadStruct作为名字的原因。如果在struct上运用了[StructLayout(LayoutKind.Explicit)],计算FieldOffset一定要小心,例如我们使用上面BadStruct来进行下面的测试:

StructExpt e  =   new  StructExpt();
e.c 
=   0 ;
e.i 
=   true ;
Console.WriteLine(e.c);

    输出的结果不再是0了,而是4.94065645841247E-324,这是因为e.c和e.i共享同一个byte,执行“e.i = true;时”也改变了e.c,CPU在按照浮点数的格式解析e.c时就得到了这个结果。(有关浮点数讨论可以参考我以前写的《精确判断一个浮点数是否等于0》)。所以在运用LayoutKind.Explicit时千万别吧FieldOffset算错了:)

 

3.[StructLayout(LayoutKind.Auto)]

    sizeof(StructAuto)得到的结果是12byte。下面来测试下这StructAuto的三个字段是如何摆放的:

unsafe
{
      StructAuto s 
= new StructAuto();
      Console.WriteLine(
string.Format("i:{0}", (int)&(s.i)));
      Console.WriteLine(
string.Format("c:{0}", (int)&(s.c)));
      Console.WriteLine(
string.Format("b:{0}", (int)&(s.b)));
}

//  测试结果:
i: 1242180
c:
1242172
b:
1242181

    即CLR会对结构体中的字段顺序进行调整,将i调到c之后,使得StructAuto的实例s占有尽可能少的内存,并进行4byte的内存对齐(Align),字段顺序调整结果如下图所示:

 


4.空struct实例的Size

struct  EmptyStruct {}

    无论运用上面LayoutKind的Explicit、Auto还是Sequential,得到的sizeof(EmptyStct)都是1byte。

 


结论:

默认(LayoutKind.Sequential)情况下,CLR对struct的Layout的处理方法与C/C++中默认的处理方式相同,即按照结构中占用空间最大的成员进行对齐(Align);

使用LayoutKind.Explicit的情况下,CLR不对结构体进行任何内存对齐(Align),而且我们要小心就是FieldOffset;

使用LayoutKind.Auto的情况下,CLR会对结构体中的字段顺序进行调整,使实例占有尽可能少的内存,并进行4byte的内存对齐(Align)。 


本文转自Silent Void博客园博客,原文链接:http://www.cnblogs.com/happyhippy/archive/2007/04/12/710927.html,如需转载请自行联系原作者

相关文章
|
存储 分布式计算 监控
阿里云服务器实例经济型e、通用算力型u1、计算型c8i、通用型g8i、内存型r8i详解与选择策略
在阿里云现在的活动中,可选的云服务器实例规格主要有经济型e、通用算力型u1、计算型c8i、通用型g8i、内存型r8i实例,虽然阿里云在活动中提供了多种不同规格的云服务器实例,以满足不同用户和应用场景的需求。但是有的用户并不清楚他们的性能如何,应该如何选择。本文将详细介绍阿里云服务器中的经济型e、通用算力型u1、计算型c8i、通用型g8i、内存型r8i实例的性能、适用场景及选择参考,帮助用户根据自身需求做出更加精准的选择。
|
存储 机器学习/深度学习 缓存
阿里云九代云服务器怎么样?计算型c9i、通用型g9i、内存型r9i实例介绍
阿里云第9代云服务器主要实例规格包括计算型c9i、通用型g9i、内存型r9i,本文将为大家介绍阿里云九代云服务器中的计算型c9i、通用型g9i、内存型r9i实例的主要性能特点,并分享最新的活动价格信息,以供参考。
1068 1
|
存储 缓存 分布式计算
高内存场景必读!阿里云r7/r9i/r8y/r8i实例架构、性能、价格多维度对比
阿里云针对高性能需求场景,一般会在活动中推出内存型r7、内存型r9i、内存型r8y和内存型r8i这几款内存型实例规格的云服务器。相比于活动内的经济型e和通用算力型u1等实例规格,这些内存型实例在性能上更为强劲,尤其适合对内存和计算能力有较高要求的应用场景。这些实例规格的云服务器在处理器与内存的配比上大多为1:8,但它们在处理器架构、存储性能、网络能力以及安全特性等方面各有千秋,因此适用场景也各不相同。本文将为大家详细介绍内存型r7、r9i、r8y、r8i实例的性能、适用场景的区别以及选择参考。
|
缓存 Linux 数据安全/隐私保护
Linux环境下如何通过手动调用drop_caches命令释放内存
总的来说,记录住“drop_caches” 命令并理解其含义,可以让你在日常使用Linux的过程中更加娴熟和自如。
2097 23
|
存储 分布式计算 安全
阿里云服务器内存型实例怎么选?r7/r8y/r8i实例性能、适用场景与选择参考
在选择阿里云服务器时,针对内存密集型应用和数据库应用,内存型实例因其高内存配比和优化的性能表现,成为了众多用户的热门选择。在目前阿里云的活动中,内存型实例主要有内存型r7、内存型r8y和内存型r8i实例可选。为了帮助大家更好地了解这三款实例的区别,本文将详细对比它们的实例规格、CPU、内存、计算、存储、网络等方面的性能,并附上活动价格对比,以便用户能够全面了解它们之间的不同,以供选择和参考。
|
机器学习/深度学习 存储 PyTorch
PyTorch内存优化的10种策略总结:在有限资源环境下高效训练模型
在大规模深度学习模型训练中,GPU内存容量常成为瓶颈,特别是在训练大型语言模型和视觉Transformer时。本文系统介绍了多种内存优化策略,包括混合精度训练、低精度训练(如BF16)、梯度检查点、梯度累积、张量分片与分布式训练、
888 14
PyTorch内存优化的10种策略总结:在有限资源环境下高效训练模型
|
存储 编解码 安全
阿里云高性能企业级甄选Intel第八代计算型c8i、通用型g8i和内存型r8i实例简介
计算型c8i、通用型g8i和内存型r8i实例是阿里云推出的高性能企业级甄选Intel第八代云服务器实例,采用CIPU+飞天技术架构,搭载最新的Intel 第五代至强可扩展处理器(代号EMR),性能进一步大幅提升,同时拥有AMX加持的AI能力增强,并在全球范围率先支持TDX机密虚拟机能力,实现了AI增强和全面安全防护的两大特色优势。本文将为您介绍这三个实例规格的性能、适用场景及最新活动价格以及选择指南,以供选择参考。
779 18
|
弹性计算 固态存储 ice
阿里云服务器ECS内存型2核16G、4核32G和8核64G配置实例、费用和性能参数表
本文整理了2025年阿里云服务器租赁价格表,涵盖2核16G、4核32G和8核64G配置收费标准。CPU内存比为1:8,提供多种实例规格如ECS内存型r8i、通用算力型u1等。价格由CPU内存、公网带宽及系统盘组成,支持优惠折扣(年付6.7折起)。文中详细列出各配置参考价格、公网带宽与系统盘收费,并对比不同实例规格性能,如Intel Xeon和AMD EPYC处理器系列,帮助用户选择高性价比方案。具体价格以阿里云官网为准。
1877 4
|
存储 缓存 数据挖掘
阿里云服务器实例选购指南:经济型、通用算力型、计算型、通用型、内存型性能与适用场景解析
当我们在通过阿里云的活动页面挑选云服务器时,相同配置的云服务器通常会有多种不同的实例供我们选择,并且它们之间的价格差异较为明显。这是因为不同实例规格所采用的处理器存在差异,其底层架构也各不相同,比如常见的X86计算架构和Arm计算架构。正因如此,不同实例的云服务器在性能表现以及适用场景方面都各有特点。为了帮助大家在众多实例中做出更合适的选择,本文将针对阿里云服务器的经济型、通用算力型、计算型、通用型和内存型实例,介绍它们的性能特性以及对应的使用场景,以供大家参考和选择。
|
存储 弹性计算 安全
阿里云服务器实例选择:经济型、通用算力型、计算型、通用型、内存型实例选择参考
当我们通过阿里云的活动购买云服务器会发现,相同配置的云服务器往往有多个不同的实例可选,而且价格差别也比较大,例如同样是4核8G的配置的云服务器,经济型e实例活动价格1595.11元/1年起,通用算力型u1实例要955.58元/1年起,而计算型c8i实例则要2845.81元/1年起,价格差别还是比较大的,因此,阿里云经济型、通用算力型、计算型、通用型、内存型实例云服务器有何差别就是很多新手用户比较关心的问题了,下面小编来为大家简单介绍下它们之间的区别。
1182 16