了解 Javascript / V8 中的字符串堆大小

Understanding String heap size in Javascript / V8

有没有人知道 understanding/explanation Javascript 和 Chrome(V8) 如何确定字符串的堆大小?

我在堆转储中看到的一些示例:

1) 具有不同 @ 对象 ID 的相同 2 个字符串(即 "dt")的多个副本均指定为 OneByteStrings。 heapdump 说每个副本都有 32 字节的浅表和保留大小。目前尚不清楚两个字节的字符串如何保留 32 的大小以及为什么这些字符串似乎没有被保留。

2) 长对象路径字符串,长度为 78 个字符。所有字符在 utf8 中都是一个字节。它被归类为 InternalizedString。它有 184 字节的保留大小。即使使用 2 字节字符编码,仍然无法解释剩余的 28 字节。为什么这些路径字符串占用这么多space?我可以想象另外 4 个字节(可能是 8 个)用于地址,另外 4 个字节用于存储字符串长度,但即使使用 2 字节字符编码,仍然会留下 16 个字节。

在内部,V8 对字符串有多种不同的表示形式:

  • SeqOneByteString:最简单,包含几个header字段,然后是字符串的字节(非UTF-8编码,只能包含前256个unicode代码点的字符)
  • SeqTwoByteString:相同,但每个字符使用两个字节(使用代理对表示不能用两个字节表示的 unicode 字符)。
  • SlicedString:其他字符串的子字符串。包含指向 "parent" 字符串的指针以及偏移量和长度。
  • ConsString:两个字符串相加的结果(如果超过一定大小)。包含指向两个字符串的指针(它们本身可能是这些类型的字符串中的任何一种)。
  • ExternalString:用于从 V8 外部传入的字符串。

"Internalized"只是一个标志,实际的字符串表示可以是以上任何一种。

它们都有一个共同的parent class String,parent是Name,parent是HeapObject(这是V8的根class 层次结构 objects 分配在 V8 堆上)。

  • HeapObject 有一个字段:指向它的 Map 的指针(对这些有很好的解释here)。
  • 名称添加了一个附加字段:哈希值。
  • 字符串添加另一个字段:长度。

在 32 位系统上,每个都是 4 个字节。在64位系统上,每个为8字节。

如果您使用的是 64 位系统,则 SeqOneByteString 的最小大小为 32 字节:24 字节用于上述 header 字段加上至少一个字节用于字符串数据,四舍五入最多 8 的倍数。

关于你的第二个问题,很难说到底是怎么回事。可能是该字符串使用 2 字节表示并且它的 header 字段将大小推高到您期望的大小以上,或者它可能是一个 ConsString 或一个 SlicedString(其保留的大小将包括它指向的字符串)。

V8 大部分时间都不会内部化字符串 - 它会内部化在解析过程中找到的字符串常量和标识符名称,以及用作 object 属性 键的字符串,可能还有其他几个案例。