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

2024-02-05

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指定恒定的字段偏移量?


我认为这是一个错误。您会看到自动布局的副作用,它喜欢将重要字段与 64 位模式下 8 字节的倍数的地址对齐。即使您明确应用该方法,也会发生这种情况[StructLayout(LayoutKind.Sequential)]属性。这是不应该发生的。

您可以通过将结构成员公开并附加测试代码来查看它,如下所示:

    var test = new RefAndTwoInt32Wrappers();
    test.text = "adsf";
    test.x.x = 0x11111111;
    test.y.x = 0x22222222;
    Console.ReadLine();      // <=== Breakpoint here

当断点命中时,使用 Debug + Windows + Memory + Memory 1. 切换到 4 字节整数并放入&test在地址字段中:

 0x000000E928B5DE98  0ed750e0 000000e9 11111111 00000000 22222222 00000000 

0xe90ed750e0是我机器上的字符串指针(不是你的)。您可以轻松地看到Int32Wrappers,加上额外的 4 个字节的填充,将大小变为 24 个字节。返回结构并将字符串放在最后。重复,你会看到字符串指针是still第一的。违反LayoutKind.Sequential, 你得到了LayoutKind.Auto.

说服微软解决这个问题是很困难的,这种方式已经运行太久了,所以任何改变都会被破坏某物。 CLR 只是尝试兑现[StructLayout]对于结构的托管版本并使其可blittable,它通常很快就会放弃。对于任何包含 DateTime 的结构来说都是臭名昭著的。只有在封送结构时才能获得真正的 LayoutKind 保证。编组版本肯定是 16 字节,如下所示Marshal.SizeOf()会告诉你的。

Using LayoutKind.Explicit修复它,而不是你想听到的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • WP8应用程序中的付款

    我目前正在制作一个 Windows Phone 8 silverlight 应用程序 在应用程序中 我们希望允许用户通过 PayPal 或其他付款方式付款 我还从开发中心查找了应用内付款 但没有在那里看到具有可变值的付款 任何有关使用什么的
  • 如何判断哪些应用程序在 GAC 中注册了给定的程序集?

    当尝试使用 gacutil exe 删除给定程序集 在本例中为 log4net dll 但它应该适用于任何类似情况 时 由于应用程序需要该程序集 操作会失败 但是 我不知道如何判断哪些应用程序实际需要它 由于输出似乎表明该要求已记录在 MS
  • PHP 水印 - Zubrag

    我正在使用 zubrags PHP 水印脚本 附在下面 它的效果很好 除非我尝试使用 PNG 24 作为我的水印 生成的图像带有乱码 不透明的水印 我想知道是否有人可以帮助解释我需要在下面的脚本中更改哪些内容 以便将 PNG 24 作为正确
  • 找不到合适的 SDK 来定位

    我尝试为 UWP 创建项目 但收到此错误 我安装了 Windows 开发工具包并且它有效 谢谢
  • 使用 $http 访问原始 XHR 对象

    我需要访问原始数据XMLHttpRequest对象在支持它的浏览器上添加文件上传进度回调 这是可能的 还是我必须自己构建原始请求 如果是这样 我该如何包装生的XMLHttpRequest在承诺对象中 我模拟了 http调用构建自定义XMLH
  • 为什么迭代器方法不能采用“ref”或“out”参数?

    我今天早些时候尝试过这个 public interface IFoo IEnumerable
  • 容器内可滚动的 div

    我有以下 HTML http jsfiddle net fMs67 http jsfiddle net fMs67 我想让 div2 尊重 div1 的大小并滚动 div3 的内容 这可能吗 Thanks 更新1 这是我在提出问题时过于简单
  • JAR 文件:找不到主类

    好吧 我有一个奇怪的问题 我想将我的程序之一作为 jar 文件运行 但是当我双击打开它时 我收到一条错误消息 例如 找不到主类 程序正在关闭 我很确定我做的一切都是正确的 罐子应该可以工作 我也尝试过其他程序 每个程序都是一样的 我通过 B
  • printf 与 std::cout [重复]

    这个问题在这里已经有答案了 可能的重复 我应该在 C 代码中使用 printf 吗 https stackoverflow com questions 2017489 should i use printf in my c code 如果我
  • 如何在一张表中创建多个序列?

    我有一张 收据 表 我有列 customer id 谁有收据 和收据号 对于每个客户 receipt number 应从 1 开始 并且是一个序列 这意味着 customer id 和receipt number 将是唯一的 我怎样才能优雅
  • VIM 自定义箭头键映射不适用于窗口切换?

    我一直在尝试创建一个在 vim 中打开的窗口拆分之间切换的快捷方式 而不是必须使用 ctrl w arrowkey 我更愿意只能够使用 ctrl arrow key 这是我当前的 vimrc 中的内容 map
  • 如何实现hbase安全批量加载

    我已经在 kerberos 集群中的 hbase 中创建了一个批量加载 其驱动程序类与此类似 工作 public static void main String args try int response ToolRunner run HB
  • PHP 传递一个类作为引用?

    在Python中 你可以这样做 class SomeClass object pass s SomeClass someClassInstance s 如何在 PHP 中实现同样的效果 据我了解 你不能这样做吗 这是真的 您可以创建动态类名
  • 向 Pandas Dataframe 中的字符串添加前导零

    我有一个 pandas 数据框 其中前 3 列是字符串 ID text1 text 2 0 2345656 blah blah 1 3456 blah blah 2 541304 blah blah 3 201306 hi blah 4 1
  • 除非填写所有文本输入字段,否则禁用表单按钮

    我有一个具有多个文本输入的表单 我不想为每个输入添加 id 因为它们是从服务器端代码生成的 字段数量可能不同等 我只是希望能够禁用提交按钮 直到出现是输入到每个文本输入中的文本 我已经做到了这一点 但仅在文本输入到一个文本输入字段之前禁用按
  • 如何使用 boost bcp?

    我有 bcp 工具 它是用 boost 安装程序预先构建的 我想将 boost 所需的依赖项提取到一个较小的文件中 因为我希望能够在学校构建这个项目 我正在尝试使用 bcp 但我不明白如何使用它 尽管有以下说明 http www boost
  • Mongodb + Node.js:删除多个文档并返回

    我使用下面的代码一次删除多个文档 db collection testcollection deleteMany id in 1 2 3 function error response 有没有办法一次性删除并返回所有已删除的文档 NOTE
  • 使用 C# 自定义属性进行异常和审计跟踪日志记录

    是否可以创建一个自定义功能来捕获由自定义属性设置的方法中发生的异常 我打算做这样的事情 Logging FeatureEnum SomeFeature IntentEnum SomeIntent some comment public vo
  • 如何构建Graceful Degradation AJAX网页?

    我想用 优雅降级 构建网页 即 即使JavaScript被禁用 网页也能正常工作 现在我必须对 AJAX 响应的格式做出设计决策 如果禁用 javascript 则对服务器的每个 HTTP 请求都会生成 HTML 作为响应 浏览器将刷新并显
  • 为什么结构对齐取决于字段类型是原始类型还是用户定义的?

    In 野田时间 http nodatime orgv2 我们正在转向纳秒分辨率 这意味着我们不能再使用 8 字节整数来表示我们感兴趣的整个时间范围 这促使我研究 Noda Time 的 许多 结构体的内存使用情况 这反过来又引导我发现 CL