在c#中以字节为单位查找对象实例的大小

Jan*_*nie 101 c#

对于任何任意实例(不同对象,组合,单个对象等的集合)

如何确定其大小(以字节为单位)?

(我现在有各种对象的集合,我正在尝试确定它的聚合大小)

编辑:有人写了一个可以做到这一点的对象的扩展方法吗?那个非常整洁的imo.

Pav*_*aev 56

首先,一个警告:接下来的内容严格地说是丑陋的,无证件的黑客攻击.不要依赖于这项工作 - 即使它现在适用于您,它可能会在明天停止工作,任何次要或主要的.NET更新.

您可以使用本文中有关CLR内部的信息 - 最后我检查过,它仍然适用.这是如何完成的(它通过TypeHandle类型检索内部"基本实例大小"字段).

object obj = new List<int>(); // whatever you want to get the size of
RuntimeTypeHandle th = obj.GetType().TypeHandle;
int size = *(*(int**)&th + 1);
Console.WriteLine(size);
Run Code Online (Sandbox Code Playgroud)

这适用于3.5 SP1 32位.我不确定64位上的字段大小是否相同 - 如果不是,则可能需要调整类型和/或偏移量.

这适用于所有"普通"类型,所有实例都具有相同的,定义良好的类型.那些不成对的是数组和字符串肯定,我也相信StringBuilder.对于它们,您将把所有包含元素的大小添加到它们的基本实例大小.

  • .NET 4版本甚至不需要不安全的代码:`Marshal.ReadInt32(type.TypeHandle.Value,4)`适用于x86和x64.我只测试了struct和class类型.请记住,这会返回值类型的*boxed*大小.@Pavel也许你可以更新你的答案. (16认同)
  • 这应该在C#中工作还是只在托管的c ++中工作?到目前为止我对C#感到不满意,我已经尝试过了:`不能获取地址,获取大小,或者声明指向托管类型的指针('System.RuntimeTypeHandle')` (3认同)
  • @ sab669很好,在他的示例中将`type`替换为`obj.GetType()`。使用哪个框架都无关紧要,只有什么CLR(v2或v4或CoreCLR)无关紧要。我还没有在CoreCLR上尝试过。 (2认同)
  • @SamGoldberg手动计算这一点需要花费大量的工作,需要处理一百万个边缘案例。Sizeof告诉您对象的静态大小,而不是对象的运行时图的内存消耗。VS2017的内存和CPU性能分析以及ReSharper的工具和其他工具都非常出色,这就是我要用来衡量的。 (2认同)

Blu*_*kMN 19

如果您正在使用可序列化对象,则可以通过假装使用二进制序列化程序对其进行序列化(但将输出路由到遗忘)来近似大小.

class Program
{
    static void Main(string[] args)
    {
        A parent;
        parent = new A(1, "Mike");
        parent.AddChild("Greg");
        parent.AddChild("Peter");
        parent.AddChild("Bobby");

        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
           new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        SerializationSizer ss = new SerializationSizer();
        bf.Serialize(ss, parent);
        Console.WriteLine("Size of serialized object is {0}", ss.Length);
    }
}

[Serializable()]
class A
{
    int id;
    string name;
    List<B> children;
    public A(int id, string name)
    {
        this.id = id;
        this.name = name;
        children = new List<B>();
    }

    public B AddChild(string name)
    {
        B newItem = new B(this, name);
        children.Add(newItem);
        return newItem;
    }
}

[Serializable()]
class B
{
    A parent;
    string name;
    public B(A parent, string name)
    {
        this.parent = parent;
        this.name = name;
    }
}

class SerializationSizer : System.IO.Stream
{
    private int totalSize;
    public override void Write(byte[] buffer, int offset, int count)
    {
        this.totalSize += count;
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override bool CanSeek
    {
        get { return false; }
    }

    public override bool CanWrite
    {
        get { return true; }
    }

    public override void Flush()
    {
        // Nothing to do
    }

    public override long Length
    {
        get { return totalSize; }
    }

    public override long Position
    {
        get
        {
            throw new NotImplementedException();
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException();
    }

    public override long Seek(long offset, System.IO.SeekOrigin origin)
    {
        throw new NotImplementedException();
    }

    public override void SetLength(long value)
    {
        throw new NotImplementedException();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 当然,这可以为您提供最小尺寸,但不会告诉您内存的大小. (6认同)
  • 就像我说的那样,它只是一个近似值.它并不完美,但我不同意它会告诉你关于内存大小的"无关".我会说它给了你*一些想法 - 更大的序列化通常会与更大的内存大小相关联.有一些*关系. (4认同)
  • 它将为您提供序列化的大小,这将是序列化程序所需的大小,以实现“序列化器”的目的。这些可能与“坐在内存中”的目的不同。例如,也许串行器将较小的整数存储在三个字节中。 (2认同)

Ari*_*amo 8

对于非托管类型,也就是值类型,结构:

        Marshal.SizeOf(object);
Run Code Online (Sandbox Code Playgroud)

对于托管对象,我得到的更接近是近似值.

        long start_mem = GC.GetTotalMemory(true);

        aclass[] array = new aclass[1000000];
        for (int n = 0; n < 1000000; n++)
            array[n] = new aclass();

        double used_mem_median = (GC.GetTotalMemory(false) - start_mem)/1000000D;
Run Code Online (Sandbox Code Playgroud)

不要使用序列化.二进制格式化程序会添加标题,因此您可以更改类并将旧的序列化文件加载到已修改的类中.

它也不会告诉你内存中的实际大小,也不会考虑内存对齐.

[编辑]通过在类的每个属性上递归使用BiteConverter.GetBytes(prop-value),您将获得以字节为单位的内容,这不会计算类或引用的权重,但更接近现实.如果大小很重要,我建议使用字节数组进行数据和非托管代理类使用指针转换来访问值,请注意这将是非对齐内存,因此在旧计算机上会很慢但是在MODERN RAM上的巨大数据集将会是更快,因为最小化从RAM读取的大小将比未对齐更大的影响.


Sam*_*ell 5

这不适用于当前的.NET实现,但要记住垃圾收集/托管运行时的一件事是对象的分配大小可以在程序的整个生命周期中发生变化.例如,一些世代垃圾收集器(例如Generational/Ulterior Reference Counting Hybrid收集器)仅需要在将对象从托儿所移动到成熟空间之后存储某些信息.

这使得无法创建可靠的通用API来公开对象大小.

  • 这取决于他们需要什么.如果对于P/Invoke(本机代码互操作),它们使用Marshal.SizeOf(typeof(T)).如果进行内存分析,它们使用与执行环境协作的单独分析器来提供信息.如果您对数组中的元素对齐感兴趣,可以在DynamicMethod中使用SizeOf IL操作码(我不认为在.NET框架中有一种更简单的方法). (2认同)

小智 5

经过优化的安全解决方案 Cyber​​Saving / MemoryUsage代码。某些情况:

/* test nullable type */      
TestSize<int?>.SizeOf(null) //-> 4 B

/* test StringBuilder */    
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) sb.Append("??????????");
TestSize<StringBuilder>.SizeOf(sb ) //-> 3132 B

/* test Simple array */    
TestSize<int[]>.SizeOf(new int[100]); //-> 400 B

/* test Empty List<int>*/    
var list = new List<int>();  
TestSize<List<int>>.SizeOf(list); //-> 205 B

/* test List<int> with 100 items*/
for (int i = 0; i < 100; i++) list.Add(i);
TestSize<List<int>>.SizeOf(list); //-> 717 B
Run Code Online (Sandbox Code Playgroud)

它也适用于类:

class twostring
{
    public string a { get; set; }
    public string b { get; set; }
}
TestSize<twostring>.SizeOf(new twostring() { a="0123456789", b="0123456789" } //-> 28 B
Run Code Online (Sandbox Code Playgroud)


Ale*_*lex 5

不直接回答问题,但对于那些有兴趣在调试时调查对象大小的人:

  1. 在 VS 中开始调试,确保显示“诊断”窗口
  2. 设置断点(可选)
  3. 暂停时单击“内存使用”中的“拍摄快照”
  4. 浏览快照(可选择按字母顺序对对象列表进行排序以找到您感兴趣的类型)

在此处输入图片说明


SLa*_*aks 4

这在运行时是不可能做到的。

不过,有多种内存分析器可以显示对象大小。

编辑:您可以编写第二个程序,使用CLR Profiling API来分析第一个程序,并通过远程处理或其他方式与其进行通信。

  • 如果在运行时不可能做到这一点,那么内存分析器如何提供信息? (22认同)
  • 那么你就会处理具有自我意识的软件,我会非常害怕。:-) 说真的,“单一职责原则”——让程序就是程序,让其他一些代码监视占用过多内存的对象。 (4认同)
  • 通过使用分析 API。然而,程序无法分析自身 (2认同)
  • @Janie:您还会对大小的重要性及其与性能的关系做出假设。我认为在这样做之前您希望成为一名真正的低级 CLR 性能专家(已经了解 Profiling API 的专家)。否则,您可能会将您之前的经验应用到它们不适用的情况中。 (2认同)