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

141

对于任意实例(包括不同对象的集合,组合,单个对象等),如何确定其占用的字节数?

(我目前有一个包含各种对象的集合,并尝试确定它们的聚合大小)

编辑:是否有人编写了一种可为Object编写的扩展方法来完成此操作?我认为那会很不错。


3
可能重复:使用C#获取字段大小(以字节为单位) - AxelEckenberger
17个回答

2
我已经为.NET中的不同集合创建了基准测试:https://github.com/scholtz/TestDotNetCollectionsMemoryAllocation 以下是在.NET Core 2.2下,分配了具有3个属性的1,000,000个对象的结果:
Testing with string: 1234567
Hashtable<TestObject>:                                     184 672 704 B
Hashtable<TestObjectRef>:                                  136 668 560 B
Dictionary<int, TestObject>:                               171 448 160 B
Dictionary<int, TestObjectRef>:                            123 445 472 B
ConcurrentDictionary<int, TestObject>:                     200 020 440 B
ConcurrentDictionary<int, TestObjectRef>:                  152 026 208 B
HashSet<TestObject>:                                       149 893 216 B
HashSet<TestObjectRef>:                                    101 894 384 B
ConcurrentBag<TestObject>:                                 112 783 256 B
ConcurrentBag<TestObjectRef>:                               64 777 632 B
Queue<TestObject>:                                         112 777 736 B
Queue<TestObjectRef>:                                       64 780 680 B
ConcurrentQueue<TestObject>:                               112 784 136 B
ConcurrentQueue<TestObjectRef>:                             64 783 536 B
ConcurrentStack<TestObject>:                               128 005 072 B
ConcurrentStack<TestObjectRef>:                             80 004 632 B

对于内存测试,我发现最好使用的是Original Answer。

GC.GetAllocatedBytesForCurrentThread()

2
据我所知,你不能仅通过深度计算每个成员的大小来确定对象的大小。但是,像集合内部的元素这样的成员的大小是否计入对象的大小,或者指向该成员的指针是否计入对象的大小,这取决于您如何定义它。
我曾经遇到过这种情况,希望能够根据它们占用的内存限制我的缓存中的对象数量。
如果有什么诀窍可以做到这一点,我会很高兴知道!

2

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


SizeOf(Object)在未来的版本中可能不再可用。请改用SizeOf<T>()。有关更多信息,请访问http://go.microsoft.com/fwlink/?LinkID=296514。 - Vinigas

2

如果您想要大致比较不同对象图/集合的大小,只需将其序列化为JSON即可 - 例如:

Console.WriteLine($"Size1:\t{(JsonConvert.SerializeObject(someBusyObject)).Length}")); Console.WriteLine($"Size2:\t{(JsonConvert.SerializeObject(someOtherObject)).Length}"));

在我的情况下,我正在测试登录时拉取的一堆 IEnumerable,我只是想大致估算它们的大小以查看它们的相对重量。

这些都是昂贵的操作,并不会直接给出堆分配大小或类似的信息,但对于我的用例来说已经足够好了,并且很容易使用。


1
正是我所需要的——测量我的物体以确定饼干大小的估计,这比这里的其他方法简单得多。而且它只有一行,并且可以很容易地在C#交互/立即窗口中使用! - Rider Harrison

1

来自Pavel和jnm2:

private int DumpApproximateObjectSize(object toWeight)
{
   return Marshal.ReadInt32(toWeight.GetType().TypeHandle.Value, 4);
}

顺便提一下,要小心,因为它只能与连续的内存对象一起使用。


1

最简单的方法是:int size = *((int*)type.TypeHandle.Value + 1)

我知道这是实现细节,但GC依赖它,需要将其与方法表的起始位置尽可能靠近以提高效率,并且考虑到GC代码的复杂性,没有人会在未来轻易更改它。事实上,它适用于每个小/大版本的.net框架+.net核心。(当前无法测试1.0)
如果您想要更可靠的方法,请使用[StructLayout(LayoutKind.Auto)]在动态程序集中发出结构体,其中包含完全相同的字段并按相同顺序排列,使用sizeof IL指令获取其大小。您可能需要在结构体内发出一个静态方法,该方法简单地返回此值。然后添加2 * IntPtr.Size作为对象头。这应该给你准确的值。
但是,如果您的类派生自另一个类,则需要分别找到每个基类的大小,并再次添加它们+ 2 * Inptr.Size作为头。您可以通过使用带有BindingFlags.DeclaredOnly标志的字段来执行此操作。
数组和字符串只需添加其长度*元素大小的大小。 对于聚合对象的累积大小,您需要实现更复杂的解决方案,其中涉及访问每个字段并检查其内容。

1

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接