.NET自定义结构类型的内存开销是多少
本文关键字:开销 多少 内存 自定义 结构 类型 NET | 更新日期: 2023-09-27 18:21:48
有一个与.NET对象相关的固定开销,正如这个SO问题中更全面地描述的那样:.NET对象的内存开销是12字节还是24字节,取决于您是在32位还是64位进程中。
也就是说,像int, double, boolean
等基本值类型不会产生开销,因为它们是值类型。
这会给您在应用程序中组合的自定义struct
类型留下什么?一方面,它们是像上面的int, double, boolean
这样的值类型[因此不应该产生开销],但另一方面,他们间接地从System.Object
派生,因此(技术上)应该产生开销。
结构的大小由其字段的大小之和决定加上使它们正确对齐的字段之间的填充,再加上结构末尾的填充,确保结构存储在数组中时它们仍然正确对齐。
因此,对于一个结构来说,它并不完全不可能包含引用类型的字段。就像一根绳子。在这种情况下,结构会更大,因为引用是引擎盖下的指针,占用8个字节而不是4个字节。
填充物是更狡猾的细节。在32位模式中,变量的对齐不能超过4。double
和long
的一个问题,8字节类型很容易被错误对齐。显著影响32位程序性能的是,如果一个double在一级缓存边界线上错位,那么读或写的速度可能会慢3倍。也是这些类型在C#内存模型中不是原子的核心原因。在64位模式下不是问题,CLR必须而且确实提供8的对齐保证。
尽管如此,CLR确实试图在32位模式下为此类结构成员提供正确的对齐方式,即使结构本身不能保证对齐。否则是具有隐式[StructLayout(LayoutKind.Sequential, Pack=8)]
属性的结构的副作用。CLR源代码中的一个奇怪之处是,执行此操作的C++语句没有注释。我怀疑这是一个快速修复不太优秀的非托管互操作性能的方法,保持structsblitable对速度非常重要。
然而,您并不总是得到这一点,如果结构包含一个成员,而该成员本身是一个没有顺序布局的结构,CLR就会放弃。值得注意的是,对于DateTime
和DateTimeOffset
,编写它们的程序员出于非常神秘的原因对它们应用了[StructLayout(LayoutKind.Auto)]属性。在DateTimeOffset的情况下,它可能是一个复制/粘贴错误。结构的布局现在将是不可预测的,它将变为LayoutKind.Auto,CLR将重新排列字段以最小化结构大小。此可能会在x64模式下导致额外填充。
这些都是模糊的实现细节,您永远不应该担心。
你放在一起的自定义结构类型在哪里你的申请?
它们与原始类型没有什么不同。除了田地之外,他们没有额外的开销。它们从object
派生的事实并不意味着它们会产生引用类型所携带的开销,即方法表指针和同步根块。
您可以使用Marshal.SizeOf
:对此进行测试
void Main()
{
var f = new Foo();
Console.WriteLine(Marshal.SizeOf(f));
}
public struct Foo
{
public int X { get; set; }
public int Y { get; set; }
}
这将以32位模式打印8,这正好是两个整数值(每个4字节)。
注意Marshal.SizeOf
将输出非托管对象的大小。CLR仍然可以自由地重新排序字段或将它们打包在一起。
您可能会对字段的对齐产生一些开销:
https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute(v=vs.100).aspx
例如
public struct Test {
public Byte A;
public long B;
}
在64位进程上的大小为16字节,在32位2(当我们预计只有9个字节时);
零。
除非由于对齐而出现间隙,否则这就是这些间隙的成本。
但除此之外,结构与字段是堆栈上布置的独立变量时的结构完全相同。
不过,将其装箱,您将通过object
处理它,它与任何其他引用类型具有相同的开销。