论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
只需一步,快速开始
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
ToB企服应用市场:ToB评测及商务社交产业平台
»
论坛
›
软件与程序人生
›
后端开发
›
Java
›
.NET对象的内存布局
.NET对象的内存布局
圆咕噜咕噜
金牌会员
|
2023-8-11 00:32:17
|
显示全部楼层
|
阅读模式
楼主
主题
930
|
帖子
930
|
积分
2790
在.NET中,理解对象的内存布局是非常重要的,这将帮助我们更好地理解.NET的运行机制和优化代码,本文将介绍.NET中的对象内存布局。
.NET中的数据类型主要分为两类,值类型和引用类型。值类型包括了基本类型(如int、bool、double、char等)、枚举类型(enum)、结构体类型(struct),它们直接存储值。引用类型则包括了类(class)、接口(interface)、委托(delegate)、数组(array)等,它们存储的是值的引用(数据在内存中的地址)。
值类型的内存布局
值类型的内存布局是顺序的,并且是紧凑的。例如,定义的结构体SampleStruct,其中包含了四个int类型字段,每个字段占用4个字节,因此整个SampleStruct结构体在内存中占用16个字节。
public struct SampleStruct
{
public int Value1;
public int Value2;
public int Value3;
public int Value4;
}
复制代码
它在内存中的布局如下:
引用类型的内存布局
引用类型的内存布局则更为复杂。首先,每个对象都有一个对象头,其中包含了同步块索引和类型句柄等信息。同步块索引用于支持线程同步,类型句柄则指向该对象的类型元数据。然后,每个字段都按照它们在源代码中的顺序进行存储。
例如,下面的类:
public class SampleStruct
{
public int Value1;
public int Value2;
public int Value3;
public int Value4;
}
复制代码
它在内存中的布局如下:
在.NET中,每个对象都包含一个对象头(Object Header)和一个方法表(Method Table)。
对象头:存储了对象的元信息,如类型信息、哈希码、GC信息和同步块索引等。对象头的大小是固定的,无论对象的大小如何,对象头都只占用8字节(在64位系统中)或4字节(在32位系统中)。
方法表:这是.NET用于存储对象的类型信息和方法元数据的数据结构。每个对象的类型,包括其类名、父类、接口、方法等都会被存储在MethodTable中。
在32位系统中,对象头和方法表指针各占4字节,因此每个对象至少占用12字节的空间(不包括对象的实例字段)。在64位系统中,由于指针的大小是8字节,但只有后4个字节被使用,每个对象至少占用24字节的空间(不包括对象的实例字段)。
每个.NET对象的头部都包含一个指向同步块的索引(Sync Block Index)和一个指向类型的指针(Type Pointer)。
Sync Block Index: 是一个指向同步块的索引。同步块用于存储对象锁定和线程同步信息的结构。当你对一个对象使用lock关键字或Monitor类进行同步时,会用到同步块。如果对象未被锁定,那么这个索引通常是0。
Type Pointer: 是一个指向对象类型MethodTable的指针。
字段按照源代码中的顺序存储。值类型的字段直接存储值,引用类型的字段存储的是对值的引用,即指针。在32位系统中,指针占用4个字节,而在64位系统中,指针占用8个字节。可以通过StructLayoutAttribute来自定义.NET中的对象内存布局。例如,通过Sequential参数可以保证字段的内存布局顺序与源代码中的相同,或者通过Explicit参数来手动指定每个字段的偏移量。实例成员需要8字节对齐,即使没有任何成员,也需要8个字节。
堆上分配对象的最小占用空间
// The generational GC requires that every object be at least 12 bytes in size.
#define MIN_OBJECT_SIZE (2*TARGET_POINTER_SIZE + OBJHEADER_SIZE)
复制代码
进阶
在.NET中,对象在内存中的布局是由运行时环境自动管理的。而对于结构体,我们可以通过System.Runtime.InteropServices命名空间的StructLayout属性来设置其在内存中的布局方式。
LayoutKind.Auto:这是类和结构的默认布局方式。在这种方式下,运行时会自动选择合适的布局。
LayoutKind.Sequential:在这种方式下,字段在内存中的顺序将严格按照它们在代码中的声明顺序。
LayoutKind.Explicit:这种方式允许你显式定义每个字段在内存中的偏移量。
以下是一个例子,它定义了一个名为SampleStruct的结构体,并使用了StructLayout属性来设置其布局方式。
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct SampleStruct
{
public byte X;
public double Y;
public int Z;
}
复制代码
在这个例子中,我们可以使用ObjectLayoutInspector库来查看SampleStruct在内存中的布局。
void Main()
{
TypeLayout.PrintLayout<SampleStruct>();
}
复制代码
上述代码的输出如下,值得注意的是,使用System.Runtime.InteropServices命名空间的StructLayout属性将结构的布局设置为Sequential。这意味着在内存中结构的布局是按照在结构中声明的字段的顺序进行的。
Type layout for 'SampleStruct'
Size: 24 bytes. Paddings: 11 bytes (%45 of empty space)
|===========================|
| 0: Byte X (1 byte) |
|---------------------------|
| 1-7: padding (7 bytes) |
|---------------------------|
| 8-15: Double Y (8 bytes) |
|---------------------------|
| 16-19: Int32 Z (4 bytes) |
|---------------------------|
| 20-23: padding (4 bytes) |
|===========================|
复制代码
这里,我们可以看到SampleStruct在内存中的具体布局:首先是X字段(占用1个字节),然后是7个字节的填充,接着是Y字段(占用8个字节),然后是Z字段(占用4个字节),最后是4个字节的填充。总共占用24个字节,其中11个字节是填充。
这个例子中,我们将结构体SampleStruct的布局设置为Auto。在这种方式下,运行时环境会自动进行布局,可能会对字段进行重新排序,或在字段之间添加填充以使他们与内存边界对齐。
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Auto)]
public struct SampleStruct
{
public byte X;
public double Y;
public int Z;
}
复制代码
如下所示再来检查SampleStruct在内存中的布局:
Type layout for 'SampleStruct'
Size: 16 bytes. Paddings: 3 bytes (%18 of empty space)
|===========================|
| 0-7: Double Y (8 bytes) |
|---------------------------|
| 8-11: Int32 Z (4 bytes) |
|---------------------------|
| 12: Byte X (1 byte) |
|---------------------------|
| 13-15: padding (3 bytes) |
|===========================|
复制代码
从输出结果可以看出,运行时环境对字段进行了重新排序,并在字段之间添加了填充。首先是Y字段(占用8个字节),然后是Z字段(占用4个字节),接着是X字段(占用1个字节),最后是3个字节的填充。总共占用16个字节,其中3个字节是填充。这种布局方式有效地减少了填充带来的空间浪费,并可能提高内存访问效率。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
本帖子中包含更多资源
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
回复
使用道具
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
圆咕噜咕噜
金牌会员
这个人很懒什么都没写!
楼主热帖
CSRF漏洞详细解读
通过Go语言创建CA与签发证书 ...
09、openfoam案例之圆柱绕流
【第86题】JAVA高级技术-网络编程5(断 ...
超融合市场,谁能当老大?
【python二级-练习题】
内网渗透之Windows认证(二) ...
SpringCloud五大常用组件
Java实现在线SQL编程【完整版】 ...
【云原生】Docker入门 -- 阿里云服务器 ...
标签云
存储
服务器
快速回复
返回顶部
返回列表