托管堆上对象的大小(Size)和Layout

2023-05-16


    前几天,我介绍了托管环境下struct实例的Layout和Size,其中介绍了StructLayoutAttribute特性,其实StructLayoutAttribute特性不只可以用在struct上,也可以用在class上,下面介绍下将StructLayoutAttribute运用在引用类型上时,对象实例的一些行为。

    在.net托管环境下,CRL像一个黑箱一样,将我们创建的对象丢在这个暗箱中进行操作,我们不能直接获得对象实例中字段的布局(Layout)和对象实例的size,但是我们可以通过Visual Studio+SOS扩展来进行非托管代码调试,并获得对象的这些信息。如果对非托管代码调试还不了解,可以参考我以前写的一篇《使用SOS - 在Visual Studio中启用非托管代码调试来支持本机代码调试》。

    默认情况下,C#编译器会在引用类型上运用[StructLayoutAttribute(LayoutKind.Auto)]特性,即按照CLR认为的最佳方式来排序实例中的字段顺序;当运用[StructLayout(LayoutKind.Sequential)]特性时,CLR会按照字段成员在被导出到非托管内存时出现的顺序依次布局,但我的测试结果是:貌似使用LayoutKind.Sequential与使用LayoutKind.Auto的结果相同;当运用[StructLayout(LayoutKind.Explicit)]时,我们可以自己设置实例中字段的位置。下面通过实例来进行分析:

None.gif // 测试代码:
None.gif
namespace  Debug
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
class ClassAuto//C#编译器会自动在上面运用[StructLayout(LayoutKind.Auto)]
ExpandedSubBlockStart.gifContractedSubBlock.gif
    dot.gif{
InBlock.gif        
public bool b1;  //1Byte
InBlock.gif
        public double d;//8byte
InBlock.gif
        public bool b2;  //1byte
ExpandedSubBlockEnd.gif
    }

InBlock.gif
InBlock.gif    [StructLayout(LayoutKind.Sequential)]
InBlock.gif    
class ClassSeqt
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
public bool b1;  //1Byte
InBlock.gif
        public double d;//8byte
InBlock.gif
        public bool b2;  //1byte
ExpandedSubBlockEnd.gif
    }

InBlock.gif
InBlock.gif    [StructLayout(LayoutKind.Explicit)]
InBlock.gif    
class ClassExpt1
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        [FieldOffset(
0)]
InBlock.gif        
public bool b1;  //1Byte
InBlock.gif
        [FieldOffset(0)]
InBlock.gif        
public double d;//8byte
InBlock.gif
        [FieldOffset(8)]
InBlock.gif        
public bool b2;  //1byte
ExpandedSubBlockEnd.gif
    }

InBlock.gif
InBlock.gif    [StructLayout(LayoutKind.Explicit)]
InBlock.gif    
class ClassExpt2
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        [FieldOffset(
0)]
InBlock.gif        
public bool b1;  //1Byte
InBlock.gif
        [FieldOffset(0)]
InBlock.gif        
public double d;//8byte
InBlock.gif
        [FieldOffset(0)]
InBlock.gif        
public bool b2;  //1byte
ExpandedSubBlockEnd.gif
    }

InBlock.gif
InBlock.gif    
static class Program
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        [STAThread]
InBlock.gif        
static void Main()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            ClassAuto deft 
= new ClassAuto();
InBlock.gif            ClassSeqt auto 
= new ClassSeqt();
InBlock.gif            ClassExpt1 expt1 
= new ClassExpt1();
InBlock.gif            ClassExpt2 expt2 
= new ClassExpt2();
InBlock.gif            
return;  //注意:在这一行设置断点
ExpandedSubBlockEnd.gif
        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif

 

    在进行测试之前,我先按照《托管环境下struct实例的Layout和Size》和《类型实例的创建位置、托管对象在托管堆上的结构》两篇文中的理论来猜测了下这四个对象的Size:
    CLR为每个对象添加SyncblkIndex和TypeHandle两个字段各占了4byte空间,合计8byte(有关SyncblkIndex和TypeHandle两个字段的讨论,可以参考《类型实例的创建位置、托管对象在托管堆上的结构》);
    对于在引用类型上应用默认的[StructLayout(LayoutKind.Auto)]特性情况,CLR应该会将两个bool型变量b1和b2放到相邻的位置中,因此ClassAuto的size应该是4(SyncblkIndex)+4(TypeHandle)+8(double d) + 1(bool b1) + 1(bool b2) + 2(4byte内存对齐)=20byte
    对于在引用类型上应用[StructLayout(LayoutKind.Sequential)]的情况,既然声明了布局顺序与声明顺序相同,则需要两个byte变量上的都要进行4byte的内存对齐,ClassSeqt的size应该是:4(SyncblkIndex)+4(TypeHandle)+1(bool b1) +3(4byte内存对齐) + 8(double d) + + 1(bool b2) + 3(4byte内存对齐)=24byte
    对于在引用类型上应用[StructLayout(LayoutKind.Explicit)]的情况,套用“在struct上应用[StructLayout(LayoutKind.Explicit)]时不会进行任何填充”的结论,ClassExpt1的size应该是4(SyncblkIndex)+4(TypeHandle)+8(double d,b1被d吃掉了) + 1(bool b2)=17byte;ClassExpt2的size应该是4(SyncblkIndex)+4(TypeHandle)+8(double d,b1和b2都被d吃掉了)=16byte
    我猜测这四个对象的内存布局图如下所示:



    上面仅仅只是我猜测的结果,但实际的测试结果并不完全是这样的,下面是测试过程:

    编译上面的代码,在Main函数的“return;”语句处设置断点,按F5进入Debug调试模式,程序运行到断点处中止;然后我们通过“菜单->Debug->Windows->Immediate”打开“Immediate Window”,在该窗口中先输入“.load sos”来启用非托管代码调试,提示已加载SOS.dll扩展后再输入“!DumpHeap -type Class”,此时会输出当前进程中类名中包含“Class”字符串的所有对象的信息,如下图所示:


    第一个表的第一列(Address)列出了这些对象的起始地址,第二列(MT)列出了类型的方法表(Method Table)的起始地址;第二个表的最后一列(Class Name)列出了这些对象的类型名,第一列(MT)列出了类型的方法表地址(跟上面这张表的第二列相同),第二列(Count)列出了当前进程中该对象实例的数量,第三列(Totel Size)内出了该类型的所有实例的总Size。下面就具体对各个对象进行分析:

1. ClassAuto:[StructLayout(LayoutKind.Auto)]

None.gif >! dumpobj 013e1a88
None.gifName: Debug.ClassAuto
None.gifMethodTable: 00a530b8
None.gifEEClass: 00a51524
None.gifSize: 
20 ( 0x14 ) bytes
None.gif (E:\Project2005\Debug\Debug\bin\Debug\Debug.exe)
None.gifFields:
None.gif      MT    Field   Offset                 Type VT     Attr    Value Name
None.gif79104f64  
4000004         c       System.Boolean   0  instance         0  b1
None.gif791059c0  
4000005          4         System.Double   0  instance  0.000000  d
None.gif79104f64  
4000006         d       System.Boolean   0  instance         0  b2

    其size和Layout跟猜想中的一致(其中第三列Offset列出了该字段的偏移位置),注意,这里是按4byte进行内存对齐,而不是像struct一样按照成员的最大size进行对齐!

2. ClassSeqt:[StructLayout(LayoutKind.Sequential)]

None.gif >! dumpobj 013e1a9c
None.gifName: Debug.ClassSeqt
None.gifMethodTable: 00a53150
None.gifEEClass: 00a51588
None.gifSize: 
20 ( 0x14 ) bytes
None.gif (E:\Project2005\Debug\Debug\bin\Debug\Debug.exe)
None.gifFields:
None.gif      MT    Field   Offset                 Type VT     Attr    Value Name
None.gif79104f64  
4000007         c       System.Boolean   0  instance         0  b1
None.gif791059c0  
4000008          4         System.Double   0  instance  0.000000  d
None.gif79104f64  
4000009         d       System.Boolean   0  instance         0  b2

    其size和Layout竟然跟使用[StructLayout(LayoutKind.Auto)]完全一致,也就是说,我上面猜测的24byte的布局是错误的,观察Offset列的值,我们可以看到,double d排在了第一个位置,bool b1排在了d的后面,也就是说CLR仍然按照[StructLayout(LayoutKind.Auto)]对字段进行布局。这是我在.net framework 2.0(Visual Studio 2005)上的测试结果,不知道其他版本的framework会不会是同样的结果-_-.

3. ClassExpt1:[StructLayout(LayoutKind.Explicit)]

None.gif >! dumpobj 013e1ab0
None.gifName: Debug.ClassExpt1
None.gifMethodTable: 00a531e8
None.gifEEClass: 00a51648
None.gifSize: 
20 ( 0x14 ) bytes
None.gif (E:\Project2005\Debug\Debug\bin\Debug\Debug.exe)
None.gifFields:
None.gif      MT    Field   Offset                 Type VT     Attr    Value Name
None.gif79104f64  400000a        
4        System.Boolean   0  instance         0  b1
None.gif791059c0  400000b        
4         System.Double   0  instance  0.000000  d
None.gif79104f64  400000c        c       System.Boolean  
0  instance         0  b2

    Layout与我们在类型定义中的设定值一致;size为20byte,说明CLR对其进行了4byte的内存对齐;

4. ClassExpt2:[StructLayout(LayoutKind.Explicit)]

None.gif >! dumpobj 013e1ac4
None.gifName: Debug.ClassExpt2
None.gifMethodTable: 00a53280
None.gifEEClass: 00a51708
None.gifSize: 
16 ( 0x10 ) bytes
None.gif (E:\Project2005\Debug\Debug\bin\Debug\Debug.exe)
None.gifFields:
None.gif      MT    Field   Offset                 Type VT     Attr    Value Name
None.gif79104f64  400000d        
4        System.Boolean   0  instance         0  b1
None.gif791059c0  400000e        
4         System.Double   0  instance  0.000000  d
None.gif79104f64  400000f        
4        System.Boolean   0  instance         0  b2

   其size和Layout跟猜想中的一致。
   四个对象的实际Layout如下图所示(根据上面表中的Offset来排的):


    最后补充一点和struct一样要注意的地方:如果在运用了[StructLayout(LayoutKind.Explicit)],计算FieldOffset一定要小心,例如我们使用上面ClassExpt1来进行下面的测试:

None.gif ClassExpt1 expt1  =   new  ClassExpt1();
None.gifexpt1.d 
=   0 ;
None.gifexpt1.b1 
=   true ;
None.gifConsole.WriteLine(expt1.d);

    输出的结果不再是0了,而是4.94065645841247E-324,这是因为expt1.b1和expt1.d共享了一个byte,执行“expt1.b1 = true;时”也改变了expt1.d,CPU在按照浮点数的格式解析expt1.d时就得到了这个结果。(有关浮点数讨论可以参考我以前写的《精确判断一个浮点数是否等于0》)。所以在运用LayoutKind.Explicit时千万别把FieldOffset算错了:)


   结论:在32位的计算机上,默认情况下,对于引用类型的实例,CLR总是按4byte进行内存对齐

转载于:https://www.cnblogs.com/happyhippy/archive/2007/04/17/717028.html

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

托管堆上对象的大小(Size)和Layout 的相关文章

  • 自动调整 ggplot 中条形的大小以实现多个图形 R 的均匀性

    我在循环中生成几个条形图 它们都根据输出大小 从绘图 设备大小假设 而不是根据条形大小调整大小 这意味着具有两个条形图的图具有粗条形图 而具有 6 个条形图的图具有细条形图 但两个输出的大小相同 下面的代码代表了我的带有可重现数据的脚本 我
  • 如何使用 Swift 从资源中加载特定图像[重复]

    这个问题在这里已经有答案了 我是 Swift 新手 我想从资源中加载特殊图像 例如我有 image 1 for iphone 4s email protected cdn cgi l email protection image 2 for
  • Android:将视图放置在任意位置

    我一直在尝试将视图放置在任意位置 My aim 覆盖 JPG PNG 的某个矩形 给定坐标与 JPG PNG 相关的 还有一些其他视图 例如图库或一些视频 我不想使用绝对布局 因为它已被弃用 因此 我使用relativelayout 定义一
  • ConstraintLayout 内的 ImageView 不起作用

    我在正确显示 ImageView 时遇到问题 我想在 ConstraintLayout 中显示 ImageView 在预览中 它看起来完全符合我的需要 但是当我在设备上启动它时 它看起来完全不同 此布局位于回收视图内 这段代码有什么问题
  • 如何在AWS批处理中定义根卷大小

    我正在使用 AWS Batch 但发现根卷大小对于我的任务来说太小 我尝试创建一个新的计算环境 作业队列 但没有任何选项来设置卷大小 我尝试更改启动配置here https console aws amazon com ec2 autosc
  • Swing 组件 - 禁用布局中的调整大小

    我有一个自定义 GUI 组件 它基于 Swing 的 JPanel 该组件放置在使用 BorderLayout 的 JFrame 中 当我调整框架大小时 该组件会不断调整大小 我怎样才能避免这种情况 我希望组件无论发生什么情况都保持相同的大
  • Kamada 和 Kawai 图形布局算法? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有人尝试过 Kamada Kawai 的 88 算法来绘制一般无向图吗 如果是这样 并且您知道其中的任
  • IE 中的表格布局错误(7)

    下面是一个带有表格布局的简单 html 代码 在 FF 中 它看起来就像我认为的那样 在 IE7 中则不然 我究竟做错了什么 我该如何解决它 table cellspacing 0 cellpadding 0 border 1 tbody
  • 如何在android listview或线性布局中动态设置marginBottom?

    friends 我想使用java代码或动态设置layout marginBottom 在列表视图或线性布局中 有人指导我如何实现这一目标吗 任何帮助 将不胜感激 ListView lst getListView LinearLayout L
  • 类似于HTML中“li”元素的红点

    我有这样的 HTML 代码 tr td align left valign bottom class leftfooter a href Customer Support a a href class footerlink About a
  • Python Tkinter OOP 布局配置

    我正在尝试使用 tkinter 构建一个应用程序 该布局在没有 OO 原则的情况下工作 但我很难理解应该如何将其转移到 OO The layout is as shown in the pic below 1280x720px 我有以下内容
  • MATLAB:比较两个不同长度的数组

    我有两个长度不同的数组 由于采样率不同 需要比较 我想对较大的数组进行下采样以匹配较小的数组的长度 但是该因子不是整数而是小数 举个例子 a 1 1 375 1 75 2 125 2 5 2 875 3 25 b 1 2 3 有什么方法可以
  • 如何在html中设置按钮的文本大小

    您好 我想在我的网站上有一个按钮 并且我想调整按钮上的文本大小 我该怎么做呢 我的代码如下
  • 响应式2列2行布局

    我一直在试图弄清楚如何创建这个布局 我有一个 2 列布局 左列有 1 行 右侧有 2 行 我试图让它流畅地调整 我遇到的问题是 我希望右侧顶部图像的顶部与左侧图像的顶部对齐 而底部图像的底部与左侧图像的底部保持对齐 我应该使用桌子吗 这是我
  • 底部页脚的 css 布局,动态 ajax 内容更改页面高度

    Update 实际上 我现在通过放弃固定页脚设计来解决这个问题 动态内容移动页脚并适当调整容器大小似乎没有问题 除非页脚最初固定在浏览器底部 我希望其他人最终能够提供一个兼具两全其美的出色解决方案 I spent all day tryin
  • Android 布局不需要的填充

    所以我有这个布局文件 如下 正如您所看到的 没有填充或边距 dimen xml 文件也没有任何填充 边距 最后 我根本不以编程方式更改布局
  • 使用 dpi 与 dp 缩放图像之间的差异

    我拥有所有由九个补丁位图组成的 dpi 可绘制目录 xxhdpi 和 xxxhdpi 是否必要 可绘制目录中的可绘制资源文件可检索所有缩放的位图 并且我使用可绘制资源文件 现在 我的问题是我还根据大小 小 正常等 创建了 缩放 布局目录 其
  • 在 mac 上使用 AVFoundation 和 AVCaptureVideoDataOutput 时如何使用自定义视频分辨率

    我需要处理捕获的视频帧的每一帧 尽管AVCaptureDevice formats提供了这么多不同尺寸的框架尺寸 看起来AVCaptureSession仅支持预设中定义的帧大小 我也尝试过设置AVCaptureDevice activeFo
  • iphone:sizeWithFont 方法无法识别字符串中的“\n”

    我使用下面的代码来计算字符串标签的大小 NSString str It s n that n don t n CGSize size str sizeWithFont UIFont fontWithName Verdana size 12
  • 带有 CollapsingToolbarLayout 的 PreferenceFragment

    我想要具有滚动活动的首选项片段 例如 Telegram 应用程序 我用了一个android support v7 widget RecyclerView in FrameLayout这是我的活动 xml

随机推荐

  • awk的使用及字符串的操作

    awk教程 awk的基本功能是对文件进行指定规则浏览和抽取信息 基本格式 xff1a 1 awk F 分隔域 39 command 39 input file s 2 写入shell脚本中 3 awk f awk script file i
  • VS2017安装后如何移动 Windows Kits文件夹

    MS的回答 LINK Try the following technique Close all programs move the Windows Kits folder to another disk for example to D
  • pytorch GPU的程序kill后未释放内存

    使用PyTorch设置多线程 xff08 threads xff09 进行数据读取 xff08 DataLoader xff09 xff0c 其实是假的多线程 xff0c 他是开了N个子进程 xff08 PID都连着 xff09 进行模拟多
  • 一个「学渣」从零Web前端自学之路

    从 13 年专科毕业开始 xff0c 一路跌跌撞撞走了很多弯路 xff0c 做过餐厅服务员 xff0c 进过工厂干过流水线 xff0c 做过客服 xff0c 干过电话销售可以说经历相当的 丰富 最后的机缘巧合下 xff0c 走上了前端开发之
  • 请求时token过期自动刷新token

    1 在开发过程中 xff0c 我们都会接触到token xff0c token的作用是什么呢 xff1f 主要的作用就是为了安全 xff0c 用户登陆时 xff0c 服务器会随机生成一个有时效性的token 用户的每一次请求都需要携带上to
  • Lua Table 长度的计算

    计算 Lua 的 Table长度 在 Lua 中 xff0c 我们可以通过这个符号 来计算字符串的长度和一个table的长度 xff0c 比如 xff1a str 61 34 I 39 am a string 34 print str 61
  • Mybatis-Plus 之BaseMapper 方法详解

    为什么80 的码农都做不了架构师 xff1f gt gt gt Mapper 继承该接口后 xff0c 无需编写 mapper xml 文件 xff0c 即可获得CRUD功能 这个 Mapper 支持 id 泛型 64 author hub
  • mac上面查看路由表

    为什么80 的码农都做不了架构师 xff1f gt gt gt 问题 本来想使用linux上面的命令route n查看mac上面的路由表的 xff0c 结果显示mac上面的route命令不是这样玩的 解决 netstat nr Mac上面需
  • el-select使用方法及遇到数据回显的坑

    2019独角兽企业重金招聘Python工程师标准 gt gt gt lt el select v model 61 34 temp lang 34 class 61 34 filter item 34 placeholder 61 34 P
  • 中国电话号码格式

    中国区号 086 北京区号010 我的电话123456 填在表格上应该如何填写呢 xff1f 手机号码应该如何填写呢 xff1f xff1f 00就不必写了 xff0c 写 43 就好了 xff0c 不同的国家国际接入的号不太一样的 xff
  • 【Quick-Cocos2d-x笔记】【一】Mac环境及相关配置

    本来是老老实实的想 xff0c 一心一意的先把C 43 43 学好 xff0c 在觉得自己C 43 43 水平还是菜鸟级的时候不要去动其他的东西 但自上次面试回来时候 xff0c 觉得这样不行啊 xff0c 虽然说现在从事的是C 43 43
  • matlab练习程序(粒子群优化PSO)

    算法没有和图像处理直接相关 xff0c 不过对于图像分类中的模式识别相关算法 xff0c 也许会用到这个优化算法 算法步骤 xff1a 1 首先确定粒子个数与迭代次数 2 对每个粒子随机初始化位置与速度 3 采用如下公式更新每个粒子的位置与
  • 您需要来自administrators的权限才能对此文件进行更改

    今天我重装了系统 xff0c 以前D盘里的一个文件夹想删除 xff0c 可以一直没法删除 xff0c 原先它提示 您需要来自 S 1 5 21 602162358 1284227242 682003330 500 的权限才能对此文件夹 xf
  • ***JAVA多线程的应用场景和应用目的举例

    多线程使用的主要目的在于 xff1a 1 吞吐量 xff1a 你做WEB xff0c 容器帮你做了多线程 xff0c 但是他只能帮你做请求层面的 简单的说 xff0c 可能就是一个请求一个线程 或多个请求一个线程 如果是单线程 xff0c
  • Kafka遇到30042ms has passed since batch creation plus linger time at org.apache.kafka.clients.producer...

    问题描述 xff1a 运行生产者线程的时候显示如下错误信息 xff1a Expiring 1 record s for XXX 0 30042 ms has passed since batch creation plus linger t
  • tcpdump -w 和 -r 的使用

    tcpdump的说明文档是这样的 xff1a w 将原始的信息包写入 形式如 tcpdump w tmp result txt 我今天试了一下 xff0c 发现其写成的文件如果用cat vim来查看的话 xff0c 都显示为乱码 经过man
  • 如何用Go访问深层嵌套的JSON数据?

    原文来自https hashnode com post how 大多数情况下 xff0c 开发人员需要使用来自其他服务的JSON数据并对其进行查询 查询JSON文档非常耗时 在过去的几天里 xff0c 我正在为Golang编写一个包 xff
  • Identity Card

    Identity Card Time Limit 2000 1000 MS Java Others Memory Limit 32768 32768 K Java Others Total Submission s 995 Accepted
  • 华为S5700交换机开启WEB配置

    近来很多朋友问关于S5700开启WEB不成功的问题 xff0c 现整理出具体步骤和命令 提示 xff1a 华为交换机配置时 xff0c 输入命令前几个字母 xff0c 按TAB可以自动补全命令 xff0c 比如在系统视图下输入sh按下TAB
  • 托管堆上对象的大小(Size)和Layout

    前几天 xff0c 我介绍了托管环境下struct实例的Layout和Size xff0c 其中介绍了StructLayoutAttribute特性 xff0c 其实StructLayoutAttribute特性不只可以用在struct上