【问题标题】:How many bytes does a string take up in x64?一个字符串在 x64 中占用多少字节?
【发布时间】:2018-09-07 13:39:53
【问题描述】:

出于学习的目的,我试图了解 C# 字符串是如何在内部存储在内存中的。

根据这个blog post,C# 字符串大小是(x64 with .NET framework 4.0):

26 + 2 * length

带有单个字符的字符串将采用 (26 + 2 * 1) / 8 * 8 = 32 bytes 。 这确实和我测得的差不多。

让我困惑的是这 26 个字节的开销是什么。

我已经运行了以下代码并检查了内存:

string abc = "abcdeg";
string aaa = "x";
string ccc = "zzzzz";

AFAIK 这些块如下:

  • 绿色:同步块(8 个字节)
  • 青色:类型信息(8 个字节)
  • 黄色:长度(4 个字节)
  • 粉红色:实际字符:每个字符 2 个字节 + NULL 终止符 2 个字节。

查看“x”字符串。确实是 32 字节(按计算)。

无论如何,如果用零填充,它看起来就像字符串的结尾。 “x”字符串可能在 NULL 终止符的两个字节之后结束,并且仍然是内存对齐的(因此是 24 个字节)。 为什么我们需要额外的 8 个字节?

我用其他(更大的)字符串大小试验了类似的结果。 看起来总是有额外的 8 个字节。

【问题讨论】:

  • 如您所知,C#(CLR) 使用反射。我不知道确切的内存打包,但它可能与反射(RTTI)有关。
  • 在 CLR v4 之前,String 类型曾经有一个额外的字段。你看m_stringLength,以前也有一个m_arrayLength。它被删除了,但他们也没有使分配缩短 4 个字节。其确切原因是神秘的,但我敢打赌,这是因为这会使许多现有的 pinvoke 代码现在破坏堆。当本机代码改变字符串时,需要传递字符串而不是 StringBuilder,这是一个非常常见的错误。
  • 在 x64 中 m_arrayLength 可以是 8 个字节吗?我发现存储数组长度的内部字段在 x64 中确实是 8 个字节。这将解释每个字符串末尾额外的 8 字节块。
  • 不,它被声明为 Int32,4 字节长。没有“内部字段”,也没有 8 个额外的字节,只生成了 4. 0 个字节(除了零终止符和这 4 个额外的字节),以使整个对象成为 8 个字节的倍数。
  • 哦,等等,这些对象根本不在堆上。它们是实习字符串,因为您在代码中使用了文字。我还没有在 CLR 中找到实习代码。考虑使用字符串表达式而不是文字来尝试它以获得更多真实性。

标签: c# string memory clr


【解决方案1】:

正如 Hans Passant 所建议的,在字符串对象的末尾添加了一个额外的字段,它是 4 个字节(在 x64 中,它可能需要另外 4 个字节用于填充)。

所以最后我们有:

= 8 (sync) + 8 (type) + 4 (length) + 4(extra field) + 2 (null terminator) + 2 * length 
= 26 + 2 * length

所以 Jon Skeet 的博文是对的(怎么可能是错的?)

【讨论】:

    猜你喜欢
    • 2012-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多