In 野田时间 http://nodatime.orgv2,我们正在转向纳秒分辨率。这意味着我们不能再使用 8 字节整数来表示我们感兴趣的整个时间范围。这促使我研究 Noda Time 的(许多)结构体的内存使用情况,这反过来又引导我发现 CLR 对齐决策中的一点奇怪之处。
首先,我意识到这is实施决策,并且默认行为可以随时更改。我意识到我can修改它使用[StructLayout] http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute and [FieldOffset] http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.fieldoffsetattribute,但我宁愿想出一个如果可能的话不需要的解决方案。
我的核心场景是我有一个struct
其中包含一个引用类型字段和另外两个值类型字段,其中这些字段是简单的包装器int
. I had hoped这将在 64 位 CLR 上表示为 16 个字节(8 个用于参考,4 个用于其他每个字节),但由于某种原因它使用 24 个字节。顺便说一句,我正在使用数组来测量空间 - 我知道布局在不同情况下可能会有所不同,但这感觉像是一个合理的起点。
这是演示该问题的示例程序:
using System;
using System.Runtime.InteropServices;
#pragma warning disable 0169
struct Int32Wrapper
{
int x;
}
struct TwoInt32s
{
int x, y;
}
struct TwoInt32Wrappers
{
Int32Wrapper x, y;
}
struct RefAndTwoInt32s
{
string text;
int x, y;
}
struct RefAndTwoInt32Wrappers
{
string text;
Int32Wrapper x, y;
}
class Test
{
static void Main()
{
Console.WriteLine("Environment: CLR {0} on {1} ({2})",
Environment.Version,
Environment.OSVersion,
Environment.Is64BitProcess ? "64 bit" : "32 bit");
ShowSize<Int32Wrapper>();
ShowSize<TwoInt32s>();
ShowSize<TwoInt32Wrappers>();
ShowSize<RefAndTwoInt32s>();
ShowSize<RefAndTwoInt32Wrappers>();
}
static void ShowSize<T>()
{
long before = GC.GetTotalMemory(true);
T[] array = new T[100000];
long after = GC.GetTotalMemory(true);
Console.WriteLine("{0}: {1}", typeof(T),
(after - before) / array.Length);
}
}
我的笔记本电脑上的编译和输出:
c:\Users\Jon\Test>csc /debug- /o+ ShowMemory.cs
Microsoft (R) Visual C# Compiler version 12.0.30501.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.
c:\Users\Jon\Test>ShowMemory.exe
Environment: CLR 4.0.30319.34014 on Microsoft Windows NT 6.2.9200.0 (64 bit)
Int32Wrapper: 4
TwoInt32s: 8
TwoInt32Wrappers: 8
RefAndTwoInt32s: 16
RefAndTwoInt32Wrappers: 24
So:
- 如果您没有引用类型字段,CLR 很乐意打包
Int32Wrapper
字段在一起(TwoInt32Wrappers
尺寸为 8)
- 即使有引用类型字段,CLR 仍然乐意打包
int
字段在一起(RefAndTwoInt32s
尺寸为 16)
- 将两者结合起来,分别
Int32Wrapper
字段似乎已填充/对齐为 8 字节。 (RefAndTwoInt32Wrappers
大小为 24。)
- 在调试器中运行相同的代码(但仍然是发布版本)显示大小为 12。
其他一些实验也得出了类似的结果:
- 将引用类型字段放在值类型字段之后没有帮助
- Using
object
代替string
没有帮助(我希望它是“任何引用类型”)
- 使用另一个结构作为引用的“包装器”没有帮助
- 使用通用结构作为引用的包装器没有帮助
- 如果我继续添加字段(为了简单起见成对添加),
int
字段仍然占 4 个字节,并且Int32Wrapper
字段占 8 个字节
- Adding
[StructLayout(LayoutKind.Sequential, Pack = 4)]
看到的每个结构都不会改变结果
有没有人对此有任何解释(最好有参考文档)或建议我如何向 CLR 提示我希望打包这些字段without指定恒定的字段偏移量?