81.C#计算对象占用内存

  1. 81.CSharp计算对象占用内存
    1. 81.1 题目
    2. 81.2 深入解析
      1. 值类型对象
      2. 引用类型对象
      3. 注意的是
      4. 示例代码
    3. 81.3 答题示例
    4. 81.4 关键词联想

81.CSharp计算对象占用内存


81.1 题目

C#中怎么计算对象占用多少内存?


81.2 深入解析

值类型对象

由其所有字段的内存空间总和,以及可能的对齐填充决定的
CLR 会对值类型进行内存对齐。内存对齐会插入填充字节,使每个字段的起始位置对齐到特定的字节边界(通常是 4 或 8 字节)

引用类型对象

引用类型对象的内存空间由其实例字段的大小、对象头的大小,以及可能的对齐填充决定
其中对象头是每个引用类型对象都有的一个对象头,包含同步块索引和类型对象指针
在32位平台上,通常占8个字节;在64位平台上,通常占16个字节

注意的是

  1. 在 32 位平台上,指针大小为 4 字节;在 64 位平台上,指针大小为 8 字节。
    这影响到对象头的大小和引用类型字段的大小
  2. 编译器可能会进行优化,以减少内存对齐所需的填充字节。不同的CLR实现可能会有所不同

示例代码

以下是计算简单对象内存大小的示例代码:

using System;
using System.Runtime.InteropServices;

class Program
{
    // 定义一个简单的类
    class MyClass
    {
        public int a;
        public double b;
    }

    static void Main()
    {
        // 计算值类型的大小
        Console.WriteLine($"int 类型的大小: {Marshal.SizeOf(typeof(int))} 字节");
        Console.WriteLine($"double 类型的大小: {Marshal.SizeOf(typeof(double))} 字节");

        // 计算引用类型的大小
        MyClass myClass = new MyClass();
        int objectHeaderSize = IntPtr.Size * 2; // 对象头的大小(同步块索引和类型对象指针)
        int instanceSize = Marshal.SizeOf(myClass.a) + Marshal.SizeOf(myClass.b); // 实例字段的大小
        int totalSize = objectHeaderSize + instanceSize; // 总大小
        Console.WriteLine($"MyClass 对象的大小: {totalSize} 字节");

        // 注意内存对齐和填充字节
        // 在32位平台上,IntPtr.Size 为 4;在64位平台上,IntPtr.Size 为 8
    }
}

81.3 答题示例

“在C#中计算对象占用的内存,需要区分值类型和引用类型,方法和组成略有不同:

对于值类型(如struct),内存大小由所有字段的总大小加上内存对齐产生的填充字节决定。例如一个包含int(4B)byte(1B)的struct,理论上5B,但因对齐(int需4B对齐),实际占8B(byte后填充3B)。可通过Marshal.SizeOf(typeof(MyStruct))获取非托管布局下的大小。

对于引用类型(如class),内存由三部分组成:

  1. 对象头(固定大小):32位平台占8B(4B同步块索引+4B类型指针),64位平台占16B(8B+8B);
  2. 实例字段总大小:所有值类型字段的大小+引用类型字段的指针大小(32位4B/64位8B);
  3. 对齐填充:确保总大小是平台字长的倍数(32位4B/64位8B)。
    计算时需累加这三部分,例如64位下一个仅含int字段的class,大小为16B(头)+4B(int)+4B(填充)=24B。

注意:Marshal.SizeOf不适用于引用类型的托管内存计算(仅反映非托管布局),托管环境下可借助GC.GetTotalMemory间接估算,或用ClrMD等工具分析实际内存布局。”


81.4 关键词联想

  • 值类型 vs 引用类型
  • 对象头(同步块索引 + 类型对象指针)
  • 内存对齐(Padding)
  • Marshal.SizeOf
  • GC.GetTotalMemory
  • 32位/64位差异(字长影响)
  • 实例字段(值类型大小 + 引用指针大小)
  • ClrMD(调试工具)
  • 非托管布局 vs 托管布局
  • 同步块索引(Sync Block Index)


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏