如何获取对象在内存中的大小?

254

我需要知道我的对象在内存中占用多少字节(C#语言)。例如,我的HashtableSortedListList<String>占用多少字节。


3
请提供需要翻译的内容。 - Jon Skeet
1
任何容器都是一个相对较小的对象,它持有对某些数据存储的引用(通常是一个数组),该数组位于实际容器对象外部,并且反过来又持有对您添加到容器中的实际对象的引用。因此,列表占用多少内存的问题甚至都没有定义清楚 - 列表对象本身的大小,由列表对象分配的内存,列表中所有内容的总大小以及在收集列表时将释放的内存量都是不同的值。 - Nir
请返回翻译后的文本:和https://dev59.com/eEbRa4cB1Zd3GeqP1paf#556061 - Pete Kirkham
请查看我创建的测试应用程序中的基准测试:https://github.com/scholtz/TestDotNetCollectionsMemoryAllocation - Scholtz
1
如果您正在使用Visual Studio,您可以使用新的.NET对象分配工具:https://devblogs.microsoft.com/visualstudio/net-object-allocation-tool-performance/ - Ricky Han
6个回答

179

这可能不是完全准确的,但对我来说足够接近了。

long size = 0;
object o = new object();
using (Stream s = new MemoryStream()) {
    BinaryFormatter formatter = new BinaryFormatter();
    formatter.Serialize(s, o);
    size = s.Length;
}

161
这将提供更多信息。它将添加DLL名称和版本,...但这并不是计算对象大小的方法。 - Aliostad
9
我同意这不是一个好的计算对象大小的方法。序列化可能是熵的一个粗略估计,但它不提供有关内存消耗的有用信息。这是一个复杂的问题,没有简单的解决方案。 - Henry Merriam
5
在 C# 中,列表被预先分配为框架设计者决定的大小,然后在需要时进行扩展。默认情况下,它可能是大约10个元素。因此,除非添加足够的元素需要分配更多内存,否则您不会注意到任何大小差异。 - Michael Yoon
2
@MatthieuCharbonnier 你在用记事本工作吗?哈哈。https://dev59.com/tl4c5IYBdhLWcg3wM36R - Rush Frisby
29
请注意,对象(o)必须标记为“可序列化”。 - Jaider
显示剩余16条评论

137

我认为你不能直接获取它,但有几种间接查找的方法。

一种方法是使用GC.GetTotalMemory方法来测量创建对象前后所使用的内存量。虽然不完美,但只要您控制应用程序的其余部分,就可以获得您感兴趣的信息。

除此之外,您可以使用分析器来获取信息,或者您可以在代码中使用profiling api来获取信息。但我认为这不容易使用。

请参阅Find out how much memory is being used by an object in C#?以获取类似问题的答案。


12
尽管“真正”的答案可能是“做不到”,但更好的答案是提供一个可能的替代方案。 - Gordon Bean
这对我很有效。在我的情况下,我正在处理一个非常复杂的对象,无法轻松序列化,因此顶部答案不可行。我使用了这种方法和for循环来获得每个点的粗略平均值。帮助我看到当这个实体不存在时与存在时的差异。Delta == 实体的大致大小。 - mBrice1024
有时候我会得到一个数字(然后与其他测试运行匹配),有时候我不会。我只是想指出这一点。(也许在再次运行测试应用程序之前GC没有完成?我不知道...) - Jan

49

3
链接已失效,请问您能否编辑您的回答? - Prolog
1
有趣的是,我已经检查了 Marshal.SizeOf(<bool 变量设置为 false>),它给了我 4... 为什么 bool 的大小是 1? - Eru
2
我认为,有1位相关,但框架仍然使用4字节或8字节(取决于您的架构)... 您可以在此处阅读详细说明 https://www.quora.com/In-C-why-is-the-size-of-the-bool-1-byte-Isnt-just-1-bit-enough - Henry

29

好的,这个问题已经得到回答并被接受,但有人要求我把我的答案放在这里。

首先,我们无法确定。这是一个内部实现细节,没有文档记录。然而,可以根据其他对象中包含的对象来推断。那么,我们如何计算缓存对象的内存需求呢?

我之前在这篇文章中提到过:

那么,我们如何计算缓存对象的内存需求呢?正如大多数人所知道的,Int32和float占用4个字节,double和DateTime占用8个字节,char实际上是两个字节(而不是一个字节),等等。字符串更加复杂,需要2*(n+1)个字节,其中n是字符串的长度。对于对象,它将取决于其成员:只需总结其所有成员的内存需求,记住所有对象引用仅是32位盒子上的4个字节指针即可。现在,这实际上并不完全正确,我们还没有处理堆中每个对象的开销。我不确定您是否需要关心此事,但我认为,如果您将使用大量小对象,则必须考虑开销。每个堆对象的成本与其原始类型相同,加上32位机器上的4个字节对象引用(尽管BizTalk在64位机器上也运行32位),再加上类型对象指针的4个字节,以及我认为同步块索引的4个字节。为什么这种额外的开销很重要?好吧,让我们想象一个有两个Int32成员的类;在这种情况下,内存需求是16字节而不是8字节。


7
这并没有回答原帖提出的问题,即我们应该如何衡量一个 HashSet 和一个 List 的大小? - yoel halb
@yoelhalb - 它确实回答了问题,而且相当精确。对于这个问题,没有一个语句或简短的答案。 - StingyJack

19

以下代码片段应该返回传递给它的任何对象的字节大小,只要它可以被序列化。 我从Quixant的一位同事那里得到了这个代码片段,用于解决在游戏平台上写入SRAM的问题。希望它能有所帮助。 感谢Carlo Vittuci。

/// <summary>
/// Calculates the lenght in bytes of an object 
/// and returns the size 
/// </summary>
/// <param name="TestObject"></param>
/// <returns></returns>
private int GetObjectSize(object TestObject)
{
    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();
    byte[] Array;
    bf.Serialize(ms, TestObject);
    Array = ms.ToArray();
    return Array.Length;
}

2
对我没用。我没有将对象类定义为可序列化,它抛出了一个“未标记为可序列化错误”。 - dreamerkumar
2
嗨,@Kevin Hirst,我尝试使用这个程序来解析我的数据集以获取字节大小。但它返回了内存不足的异常。我发现字节的最大大小是2 GB?你有什么管理它的想法吗? - Worgon
@Worgon,你真的需要知道你的数据集有多大吗,除了调查目的之外?我宁愿从更高的层面考虑,消除这种必要性。如果你坚持,你可以考虑测量特定数据行的大小或使用上述的GC.GetTotalMemory方法。 - Alexey Khoroshikh
@Worgon 2GB是.NET环境下应用程序的内存大小。你无法轻松地管理它,只能将大对象存储在其他堆中。 - VMAtm
1
这是最受欢迎的答案的副本,它有问题,但是这段代码有几个问题,首先binaryformatter很慢,然后是没有使用的memory stream,再加上数据复制到数组中。 - NiKiZe

2
在调试模式下,加载SOS并执行dumpheap命令。

9
看起来像是Windbg的东西,它可能非常有用。您能详细说明如何在Visual Studio中完成吗? - Arsen Zahray
2
这是在Visual Studio中如何实现的:https://dev59.com/qXNA5IYBdhLWcg3wAI3d#66929670 - Alex from Jitbit

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