首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么结构对齐取决于字段类型是原始类型还是用户定义的类型?

为什么结构对齐取决于字段类型是原始类型还是用户定义的类型?
EN

Stack Overflow用户
提问于 2014-07-15 01:30:46
回答 4查看 7.9K关注 0票数 123

Noda Time v2中,我们将转向纳秒分辨率。这意味着我们不能再使用8字节整数来表示我们感兴趣的整个时间范围。这促使我调查Noda Time的(许多)结构的内存使用情况,这反过来又让我发现了CLR对齐决策中的一个小小的奇怪之处。

首先,我意识到这是一个实现决策,默认行为随时可能改变。我意识到我可以使用[StructLayout][FieldOffset]修改它,但如果可能的话,我宁愿提出一个不需要这样做的解决方案。

我的核心场景是,我有一个包含一个引用类型字段和另外两个值类型字段的struct,其中这些字段是int的简单包装器。我曾希望在64位CLR上将其表示为16个字节(8个用于引用,4个用于其他引用),但由于某种原因,它使用了24个字节。顺便说一句,我是用数组来测量空间的--我知道在不同的情况下布局可能会不同,但这感觉像是一个合理的起点。

下面是一个演示该问题的示例程序:

代码语言:javascript
复制
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);
    }
}

以及我笔记本电脑上的编译和输出:

代码语言:javascript
复制
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

所以:

  • 如果你没有一个引用类型字段,CLR很乐意把Int32Wrapper字段打包在一起(TwoInt32Wrappers的大小是8)
  • 即使有一个引用类型字段,CLR也很乐意把int字段打包在一起(RefAndTwoInt32s有一个引用类型字段的大小)这两个字段,每个Int32Wrapper字段看起来都被填充/对齐到8个字节。(RefAndTwoInt32Wrappers的大小为24。)在调试器(但仍是发布版本)中运行相同代码的
  • 显示大小为12。

其他一些实验也产生了类似的结果:

  • 将引用类型字段放在值类型字段之后对
  • 使用object而不是string没有帮助(我期望它是“任何引用类型”)
  • 使用另一个结构作为引用的“包装器”对

<代码>H132使用泛型结构作为引用的包装器没有帮助<代码>H233<代码>H134如果我继续添加字段(为简单起见),<代码>D35字段仍然是4个字节,和Int32Wrapper字段的大小为8个字节,

  • [StructLayout(LayoutKind.Sequential, Pack = 4)]添加到可见的每个结构中不会更改结果

有没有人对此有任何解释(最好是参考文档),或者我如何才能向CLR提示我希望在不指定常量字段偏移量的情况下打包字段?

EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24742325

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档