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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1128315/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 08:51:46  来源:igfitidea点击:

Find size of object instance in bytes in c#

c#

提问by Janie

For any arbitrary instance (collections of different objects, compositions, single objects, etc)

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

How can I determine its size in bytes?

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

(I've currently got a collection of various objects and i'm trying to determine the aggregated size of it)

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

EDIT: Has someone written an extension method for Object that could do this? That'd be pretty neat imo.

编辑:是否有人为 Object 编写了可以执行此操作的扩展方法?那将是非常整洁的imo。

回答by SLaks

This is impossible to do at runtime.

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

There are various memory profilers that display object size, though.

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

EDIT: You could write a second program that profiles the first one using the CLR Profiling APIand communicates with it through remoting or something.

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

回答by Charles Prakash Dasari

AFAIK, you cannot, without actually deep-counting the size of each member in bytes. But again, does the size of a member (like elements inside a collection) count towards the size of the object, or a pointer to that member count towards the size of the object? Depends on how you define it.

AFAIK,如果不实际深入计算每个成员的大小(以字节为单位),就不能。但同样,成员的大小(如集合中的元素)是否计入对象的大小,或者指向该成员的指针计入对象的大小?取决于你如何定义它。

I have run into this situation before where I wanted to limit the objects in my cache based on the memory they consumed.

在我想根据它们消耗的内存限制缓存中的对象之前,我遇到过这种情况。

Well, if there is some trick to do that, I'd be delighted to know about it!

好吧,如果有一些技巧可以做到这一点,我很高兴知道!

回答by Charlie

You can use reflection to gather all the public member or property information (given the object's type). There is no way to determine the size without walking through each individual piece of data on the object, though.

您可以使用反射来收集所有公共成员或属性信息(给定对象的类型)。但是,如果不遍历对象上的每个单独数据,就无法确定大小。

回答by Sam Harwell

This doesn't apply to the current .NET implementation, but one thing to keep in mind with garbage collected/managed runtimes is the allocated size of an object can change throughout the lifetime of the program. For example, some generational garbage collectors (such as the Generational/Ulterior Reference Counting Hybrid collector) only need to store certain information after an object is moved from the nursery to the mature space.

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

This makes it impossible to create a reliable, generic API to expose the object size.

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

回答by BlueMonkMN

You may be able to approximate the size by pretending to serializing it with a binary serializer (but routing the output to oblivion) if you're working with serializable objects.

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

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();
    }
}

回答by Mehrdad Afshari

For value types, you can use Marshal.SizeOf. Of course, it returns the number of bytes required to marshal the structure in unmanaged memory, which is not necessarily what the CLR uses.

对于值类型,您可以使用Marshal.SizeOf. 当然,它返回在非托管内存中封送结构所需的字节数,这不一定是 CLR 使用的。

回答by Pavel Minaev

First of all, a warning: what follows is strictly in the realm of ugly, undocumented hacks. Do not rely on this working - even if it works for you now, it may stop working tomorrow, with any minor or major .NET update.

首先,警告:以下内容严格涉及丑陋、无证的黑客攻击。不要依赖这个工作 - 即使它现在对你有用,它可能会在明天停止工作,因为任何次要或主要的 .NET 更新。

You can use the information in this article on CLR internals MSDN Magazine Issue 2005 May - Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects- last I checked, it was still applicable. Here's how this is done (it retrieves the internal "Basic Instance Size" field via TypeHandleof the type).

您可以使用有关 CLR 内部结构MSDN 杂志 2005 年 5 月 - 深入了解 .NET Framework 内部结构以了解 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);

This works on 3.5 SP1 32-bit. I'm not sure if field sizes are the same on 64-bit - you might have to adjust the types and/or offsets if they are not.

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

This will work for all "normal" types, for which all instances have the same, well-defined types. Those for which this isn't true are arrays and strings for sure, and I believe also StringBuilder. For them you'll have add the size of all contained elements to their base instance size.

这将适用于所有“普通”类型,所有实例都具有相同的、明确定义的类型。那些不是真的是数组和字符串,我也相信StringBuilder. 对于它们,您需要将所有包含元素的大小添加到它们的基本实例大小。

回答by weston

Use Son Of Strikewhich has a command ObjSize.

使用具有命令的Strike 之子ObjSize

Note that actual memory consumed is always larger than ObjSizereports due to a synkblkwhich resides directly before the object data.

请注意,ObjSize由于 asynkblk位于对象数据之前,因此实际消耗的内存始终大于报告。

Read more about both here MSDN Magazine Issue 2005 May - Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects.

在此处阅读有关两者的更多信息MSDN Magazine Issue 2005 May - Drill Into .NET Framework Internals 以了解 CLR 如何创建运行时对象

回答by Aridane álamo

For unmanaged types aka value types, structs:

对于非托管类型又名值类型,结构:

        Marshal.SizeOf(object);

For managed objects the closer i got is an approximation.

对于托管对象,我得到的越接近是一个近似值。

        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;

Do not use serialization.A binary formatter adds headers, so you can change your class and load an old serialized file into the modified class.

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

Also it won't tell you the real size in memory nor will take into account memory alignment.

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

[Edit] By using BiteConverter.GetBytes(prop-value) recursivelly on every property of your class you would get the contents in bytes, that doesn't count the weight of the class or references but is much closer to reality. I would recommend to use a byte array for data and an unmanaged proxy class to access values using pointer casting if size matters, note that would be non-aligned memory so on old computers is gonna be slow but HUGE datasets on MODERN RAM is gonna be considerably faster, as minimizing the size to read from RAM is gonna be a bigger impact than unaligned.

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

回答by AlexPalla

safesolution with some optimizations CyberSaving/MemoryUsage code. some case:

具有一些优化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

It works also with classes:

它也适用于类:

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