首页 > 系统相关 >探究C#中Class和Struct互相嵌套的内存问题

探究C#中Class和Struct互相嵌套的内存问题

时间:2023-06-10 18:34:46浏览次数:39  
标签:Struct C# Class 内存 字节 public 指针

内存分区

先回顾一下C#的内存种类

  1. 栈区:由编译器自动分配释放 ,存放值类型的对象本身,引用类型的引用地址(指针),静态区对象的引用地址(指针),常量区对象的引用地址(指针)等。其操作方式类似于数据结构中的栈。

  2. 堆区(托管堆):用于存放引用类型对象本身。在c#中由.net平台的垃圾回收机制(GC)管理。栈,堆都属于动态存储区,可以实现动态分配。

  3. 静态区及常量区:用于存放静态类,静态成员(静态变量,静态方法),常量的对象本身。由于存在栈内的引用地址都在程序运行开始最先入栈,因此静态区和常量区内的对象的生命周期会持续到程序运行结束时,届时静态区内和常量区内对象才会被释放和回收(编译器自动释放)。所以应限制使用静态类,静态成员(静态变量,静态方法),常量,否则程序负荷高。

  4. 代码区:存放函数体内的二进制代码。

内存分配

值类型分配在栈上,引用类型分配在堆上,这个概念一般都是比较清楚的。但是遇到下面的情况内存又是如何分配的呢?

Struct中嵌套Class对象

using System.Runtime.InteropServices;

namespace MyNamespace
{
    namespace MyNamespace
{
    public class B
    {
        public int x;
        public byte y;
    }

    public struct A
    {
        public byte x;
        public B b;
    }

    public class Test
    {
        static A a;
        public static void Main()
        {
            a = new A()
            {
                x = 1,
                b = new B() { x = 1, y = 1 },
            };

            System.Console.WriteLine(Marshal.SizeOf(a));
        }
    }
}

思考一下打印出来的值会是多少?

32位系统:

64位:

原因:

  1. 不同的编译器(x86、x64)对应的指针大小不一样。x86指针大小4个字节,x64指针大小为8个字节
  2. struct中存储Class,存储的是类的指针引用。整个Struct开辟在栈上,但是真正的类内存依旧是开辟在堆上。
  3. struct存在内存对齐的问题(提高访问速度)。内存对齐会向内存最大的那个对齐,即测试代码中,A.x会向(A.b的指针)大小对齐。

结论:Struct中嵌套Class引用对象,持有的是Class的引用指针。

Claas中嵌套Struct

using System.Runtime.InteropServices;

namespace MyNamespace
{
    public class B
    {
        public int x;
        public A y;
    }

    public struct A
    {
        public int x;
        public byte y;
    }

    public class Test
    {
        static B b;
        public static void Main()
        {
            b = new B()
            {
                x = 1,
                y = new A() { x = 1, y = 1 },
            };
        }
    }
}

通过对堆内存进行快照

32位:

64位:

出乎意料的内存大小!为什么会是这样的内存大小,我们先研究一下如果仅仅只有一个空对象,这个对象在内存中的大小是多少呢?我们通过查阅.Net官方文档看一下,一个对象是如何分配的:

引用类型变量(如smallObj)以固定大小(4字节)存储在栈上,并指向在托管堆上分配的对象实例的地址。 smallObj的实例包含指向相应类型的MethodTable的TypeHandle(类型对象指针)和syncblk index(同步块索引,用来做线程同步的,这里就不详细讲了,大家可以去原文查看)。最后当一个类没有定义任何实例字段,它将产生4个字节的开销(用于分配到栈上来对他进行引用)。这样我们就可以算出32位下一个空对象的内存大小了:4(syncblk index)+ 4(TypeHandle)+ 4(Instance Fields)= 12个字节。同理在64位下将会是24个字节。


返回到上面的测试用例,这时候我们就明白为什么是这样的内存分配大小了。32位下:基础的12个字节 + 4字节(int)+ 4字节(Struct指针) = 20个字节。64位下的32个字节留给大家思考是怎么样一个组成的呢?

总结

  • Struct内部持有的是Class的指针
  • Class内部持有的也是Struct的指针,但是这些都是开辟在堆上的
  • Class需要分配syncblk index和TypeHandle,用来进行同步索引和类型查询,在考虑Class的内存开销的时候需要考虑进去。

标签:Struct,C#,Class,内存,字节,public,指针
From: https://www.cnblogs.com/fzuljz/p/17262403.html

相关文章

  • Luogu P4159 [SCOI2009] 迷路
    [SCOI2009]迷路题目背景windy在有向图中迷路了。题目描述该有向图有\(n\)个节点,节点从\(1\)至\(n\)编号,windy从节点\(1\)出发,他必须恰好在\(t\)时刻到达节点\(n\)。现在给出该有向图,你能告诉windy总共有多少种不同的路径吗?答案对\(2009\)取模。注意:windy......
  • Mac 修改 HostName 和用户名
    Mac修改HostName和用户名修改HostName:zhpj@zhpj~%sudoscutil--setHostNamelocalhostzhpj@zhpj~%重新打开终端:Lastlogin:SatJun1012:45:31onttys001jason@localhost~%‍修改用户名参照视频(最后的两分钟):修改Mac电脑的用户名,最简单的操作,却蕴含......
  • CentOS 7.9 安装指南
    基于https://linux.cn/article-8048-1.html进行写的,避免原文失效下载地址:https://mirrors.aliyun.com/centos/这里有多个版本,我们选择7.9.2009,找到这个目录/centos/7.9.2009/isos/x86_64/我下载的是CentOS-7-x86_64-DVD-2009.iso其他版本不确定是否好用。然后用bale......
  • My First CRUD App With Fast API
    ImagebyauthorIt’sthedaybeforemyfastAPIlivestream.Idecidedtocreateaguideforthestreamandturnitintoablogpost.So,hereitis.Streamstructure:FastAPIvsDjangoInstallationCreateFirstAPP&RouteSwaggerUIPathParamete......
  • linux环境下使用压缩包、docker、docker-compose搭建nacos集群
    本篇文章介绍如何在linux系统上安装nacos和搭建集群,本文章使用的是centos7系统。本机搭建nacos1、准备工作Nacos依赖 Java需要三台linux服务器,条件有限的可以安装虚拟机,内存有限那就只需要安装一台虚拟机,然后开启不同端口就可以,所以最低的条件是你需要安装一个centos7版本的虚拟......
  • RD client远程桌面使用教程手机怎么连接服务器远程桌面?
    大家好啊,现在很多游戏、应用为了体验感好、方便使用都实现了多端同步,服务器远程连接也不例外,今天小驰给你们分享手机连接服务器远程桌面的操作方法不管是安卓还是苹果手机,咱们都需要用到一个软件,叫做RDclient(RemoteDesktop),先从安卓讲起安卓版RDclient下载地址:https://pan.baid......
  • [CTSC1997] 选课(树状DP)
    刚接触树状DP,好难啊QAQ[CTSC1997]选课题目描述在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 N 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是......
  • 使用clickhouse和mysql查询时间对比
    业务场景,对于数据量过大的数据统计,跑脚本会很吃力先建立一个clickhouse的mysql引擎表关联本地mysql数据表,以下这个表会自动同步mysql主表数据CREATETABLEtest_table(idUInt32,messageString,contentString,remarkString,order_idString,user_idUInt......
  • useEffect的的用法
    useEffect主要两个用途,一个是执行组件初次渲染时执行的函数,另一个是监听变化时执行相关函数;语法是useEffect(setup,dependencies?)setup是要执行的函数,dependencies是要监听的对象,可以是组件中定义的属性、状态、变量、方法,当没有dependencies时,该钩子只在组件初次渲染时执行对......
  • Python+sklearn使用DBSCAN聚类算法案例一则
    DBSCAN聚类算法概述:DBSCAN属于密度聚类算法,把类定义为密度相连对象的最大集合,通过在样本空间中不断搜索最大集合完成聚类。DBSCAN能够在带有噪点的样本空间中发现任意形状的聚类并排除噪点。DBSCAN算法不需要预先指定聚类数量,但对用户设定的参数非常敏感。当空间聚类的密度不均匀、......