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

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(使用前将#替换为@)

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

  • 计算 Richtextbox 中所有单词的最有效方法是什么?

    我正在编写一个文本编辑器 需要提供实时字数统计 现在我正在使用这个扩展方法 public static int WordCount this string s s s TrimEnd if String IsNullOrEmpty s re
  • 提交后禁用按钮

    当用户提交付款表单并且发布表单的代码导致 Firefox 中出现重复发布时 我试图禁用按钮 去掉代码就不会出现这个问题 在firefox以外的任何浏览器中也不会出现这个问题 知道如何防止双重帖子吗 System Text StringBui
  • 在 DataView 的 RowFilter 中选择 DISTINCT

    我试图根据与另一个表的关系缩小 DataView 中的行范围 我使用的 RowFilter 如下 dv new DataView myDS myTable id IN SELECT DISTINCT parentID FROM myOthe
  • MVC 在布局代码之前执行视图代码并破坏我的脚本顺序

    我正在尝试将所有 javascript 包含内容移至页面底部 我正在将 MVC 与 Razor 一起使用 我编写了一个辅助方法来注册脚本 它按注册顺序保留脚本 并排除重复的内容 Html RegisterScript scripts som
  • 在 LINQ 中按 Id 连接多表和分组

    我想按categoryId显示列表产品的名称组 这是我的代码 我想要我的视图显示结果 Desktop PC HP Red PC Dell Yellow PC Asus Red SmartPhone Lumia 720 Blue 我的组模型
  • 错误:表达式不产生值

    我尝试将以下 C 代码转换为 VB NET 但在编译代码时出现 表达式不产生值 错误 C Code return Fluently Configure Mappings m gt m FluentMappings AddFromAssemb
  • 如何在加载.NET WinForm应用程序user.config文件时捕获异常?

    有时 在使用默认配置系统的 NET 2 0 WinForm 桌面应用程序中 user config文件将被损坏并且无法再加载 当配置系统尝试加载它时 它会抛出一个System Xml XmlException 抛开 为什么文件首先被损坏 的
  • 如何区分用户点击链接和页面自动重定向?

    拥有 C WebBrowser control http msdn microsoft com en us library system windows forms webbrowser aspx在我的 WinForms 应用程序中 并意识
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 我可以使用 moq Mock 来模拟类而不是接口吗?

    正在经历https github com Moq moq4 wiki Quickstart https github com Moq moq4 wiki Quickstart 我看到它 Mock 一个接口 我的遗留代码中有一个没有接口的类
  • DbContext 和 ObjectContext 有什么区别

    From MSDN 表示工作单元和存储库模式的组合 使您能够查询数据库并将更改分组在一起 然后将这些更改作为一个单元写回存储 DbContext在概念上类似于ObjectContext 我虽然DbContext只处理与数据库的连接以及针对数
  • Qt - ubuntu中的串口名称

    我在 Ubuntu 上查找串行端口名称时遇到问题 如您所知 为了在 Windows 上读取串口 我们可以使用以下代码 serial gt setPortName com3 但是当我在 Ubuntu 上编译这段代码时 我无法使用这段代码 se
  • 如何在 Xaml 文本中添加电子邮件链接?

    我在 Windows Phone 8 应用程序中有一些大文本 我希望其中有电子邮件链接 例如 mailto 功能 这是代码的一部分
  • C#:帮助理解 UML 类图中的 <>

    我目前正在做一个项目 我们必须从 UML 图编写代码 我了解 UML 类图的剖析 但我无法理解什么 lt
  • 外键与独立关系 - Entity Framework 5 有改进吗?

    我读过了several http www ladislavmrnka com 2011 05 foreign key vs independent associations in ef 4 文章和问题 https stackoverflow
  • 将 MQTTNet 服务器与 MQTT.js 客户端结合使用

    我已经启动了一个 MQTT 服务器 就像this https github com chkr1011 MQTTnet tree master例子 该代码托管在 ASP Net Core 2 0 应用程序中 但我尝试过控制台应用程序 但没有成
  • 方法优化 - C#

    我开发了一种方法 允许我通过参数传入表 字符串 列数组 字符串 和值数组 对象 然后使用这些参数创建参数化查询 虽然它工作得很好 但代码的长度以及多个 for 循环散发出一种代码味道 特别是我觉得我用来在列和值之间插入逗号的方法可以用不同的
  • 无法接收 UDP Windows RT

    我正在为 Windows 8 RT 编写一个 Windows Store Metro Modern RT 应用程序 需要在端口 49030 上接收 UDP 数据包 但我似乎无法接收任何数据包 我已按照使用教程进行操作DatagramSock
  • Oracle Data Provider for .NET 不支持 Oracle 19.0.48.0.0

    我们刚刚升级到 Oracle 19c 19 3 0 所有应用程序都停止工作并出现以下错误消息 Oracle Data Provider for NET 不支持 Oracle 19 0 48 0 0 我将 Oracle ManagedData
  • 当从finally中抛出异常时,Catch块不会被评估

    出现这个问题的原因是之前在 NET 4 0 中运行的代码在 NET 4 5 中因未处理的异常而失败 部分原因是 try finallys 如果您想了解详细信息 请阅读更多内容微软连接 https connect microsoft 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