编程语言研究书库

03 V8 的字符串内幕:同一张规范,好几种存法

03 V8 的字符串内幕:同一张规范,好几种存法

这一章讲什么

第二章搞清了字符串「是什么」:一串 16 位整数,可能是文字也可能不是。 这一章回答下一个问题:V8 实际怎么存它? 答案颠覆「一个字符串一块连续内存」的直觉: V8 内部同时维护着六七种字符串表示,按内容、大小、用法自动切换; 拼接两个字符串经常一个字节都不用复制,只存两个指针。 而切换的开关握在你手里——你写代码的方式,决定 V8 能不能用上这些优化。

本章的场景贯穿始终:用 JavaScript 生成 HTML 模板(把 <h1>{{title}}</h1> 这样的 骨架和数据拼成最终页面),这是每个做 Web 的人都干过的活,也是字符串性能问题的 最佳演示场。

顶层全景:字符串的三级存储

  你的字符串
     │ 小于 128 KB?
     ├─ 是 → 普通页(page,约 256 KB 一块)里的堆对象,和别的对象挤在一起
     └─ 否 → Large Object Space:独占一页,垃圾回收时永不挪动
     │
     │ 内容只含 Latin1 字符?
     ├─ 是 → 单字节表示(每字符 1 字节)
     └─ 否 → 双字节表示(每字符 2 字节,装全量 UTF-16)
     │
     │ 怎么来的?
     ├─ 小字符串直接量 → 内部化(全局只存一份)
     ├─ a + b 拼出来的 → ConsString:只存两个指针
     ├─ s.slice() 切出来的 → SlicedString:存 母串+偏移+长度
     └─ 用到内容时 → 惰性展平(flatten)成一块连续内存的 SEQ 字符串

  图说:同一个 'hello world',在 V8 眼里可能是 6 种东西中的某一种;你看不出来,但它会体现在性能上。

核心原理

3.1 一切的起点:字符串不可变

先把这块地基打了:JavaScript 字符串创建之后就不能改——书里的术语叫 不可变(immutable)。你想改?str[0] = 'H' 这样的赋值会静默失败, 字符串还是原样;所谓「修改字符串」的操作,实际都是造一个新字符串1。

不可变是把双刃剑。坏处是 result += 'a' 循环一万次,会造出一万个中间字符串2。 好处是引擎敢大胆共享:既然一个字符串永远不会变,V8 就可以让许多变量(有名有姓的值容器)指向 同一份内容而不用担心谁把它改了——后面所有优化(ConsString、SlicedString、 ThinString、内部化)都建立在这上面3。

书里的配套建议也就一句话:需要反复拼接时,别用 +=,用 Array.join() 或模板字面量(`...${...}...` 那种写法)4。

3.2 反模式一:循环里 += —— 二次方是怎么长出来的

第一个反模式,几乎每个教程都警告过,但书里给了它精确的账。场景: 生成一张 25,000 行用户的 HTML 表格,每行大约 50–80 字节,循环里 html += ...5。

主线例子:累加十行的账。 假设每行长 10 个字节,循环里 += 每转一圈都造一个新字符串, 把目前全部内容复制一遍(因为不可变,没法在原地追加):

圈数新串长度本轮复制的字节累计复制
1101010
2202030
3303060
…………
n10n10n10×(1+2+…+n) = 5n(n+1)

(表里每行长 10 字节是为演示编的;真实页面每行 50–80 字节,账同比例放大。) 第 n 圈要复制的东西跟 n 成正比,复制总次数也跟 n 成正比,乘起来就是平方级: 抄写的总字节数随项目数的平方增长6。回到真实数据:表格成品约 1.9 MB, 但抄写过程中的累计内存分配是另一个数量级——原书给的嵌套循环版本里, 成品约 2.4 MB,构造过程中的内存峰值却能到 12 MB,是成品的 5 倍7。

教科书里管这个叫复杂度,也就是代价随输入规模的增长速度。它的具体长相: 不是程序变慢一点点,而是数据量每翻一倍,代价翻四倍。

3.3 反模式二:递归(函数自己调自己)拼串,和反模式三:无界缓存

第二个反模式是递归(函数自己调自己)构造。场景换成生成嵌套导航菜单:一个根菜单下 50 个子菜单、 每个子菜单 30 个项,递归函数一层层往返回的字符串上 +=8。账同样不平: 成品只有约 244 KB,过程中的内存消耗却能超过 1.2 MB——又是五倍9。 问题不在递归本身,在于每一层递归返回时,又要把下面所有层拼好的内容整体复制一遍, 平方级换个姿势再来一次。

第三个反模式更隐蔽,因为它长得像个优化:缓存。前两 个反模式让代码又慢又吃内存, 开发者自然会想「那就算一次存起来」。书里的例子是一个 Map 当缓存, 只进不出10。缓存的固有代价是:东西被留在内存里的时间变长; 而「无界」(不限制总量)的缓存,会把「临时峰值」变成「常驻占用」—— 应用跑多久,这些大字符串就占多久。

作者讲了一个亲历的生产事故收尾:有客户的系统用缓存去重并发请求, 大约 15% 的请求会失败,而代码只在成功时才把条目移出缓存—— 失败条目永远留在里面,直到内存耗尽。这个 bug 在线上跑了一年; 修法是每 30 秒把整个缓存清空一次——治标的 hack 代替了治本的修复11。 这个故事的教训和字符串无关,和所有的缓存都有关:缓存必须有界、必须有出口。

3.4 V8 的内存世界观:堆、页、Large Object Space

要懂 V8 怎么救场,先认识三个存储概念(内存管理的大部头在后面章节,这里只取够用部分)12:

堆(heap)是分给每个 JavaScript 进程的一大片内存,程序运行时造的对象、 字符串、数组都住这里——可以把它想成一个总仓库13。

页(page)是仓库里的标准货架:固定大小,通常每块约 256 KB。 所有堆数据都拆散存放在各页上,一页里可以塞许多互不相干的小对象14。

Large Object Space(大对象区)是仓库里另一种货架:专供大物。 任何东西大到放不进标准页,就独享一页(至少装得下它)15。

V8 把字符串当堆对象存:小于 128 KB 的算小字符串,住普通页; 超过 128 KB 的算大字符串,进 Large Object Space16。 大对象区的对象在垃圾回收(引擎自动清理不再使用的内存的机制)时不会被挪动—— 它们钉死在原地。代价是内存碎片化:仓库里出现越来越多的「占着整层的大件」, 缝隙塞不进新东西,高内存占用就这么持续下去17。 反模式 3.2、3.3 里那些几 MB 的巨型中间字符串,全都会落进这个区: 你构造字符串的方式,直接决定 V8 怎么摆放、怎么回收内存18。

还有个规格注脚:规范允许字符串长到 2^53-1 个元素(理论上 1800 万 TB), 但 V8 和所有引擎都设了实际上限——32 位机约 256 MB,64 位机约 512 MB19。 规范的天空很大,引擎的天花板很低,这就是「语言定义」和「实现定义」的又一例。

3.5 主角登场:ConsString —— 拼接不复制,只存两个指针

现在看 V8 的第一件救场工具。规范说,拼接 foo + bar 就是把两份码元「排在一起」20。 教科书式做法:开一块 foo 和 bar 合计长度的新内存,把两边各抄一遍—— 刚才表格例子的平方级灾难就是这么来的。

V8 的做法:开一个新对象,里面只放两个指针,分别指向 foo 和 bar,一个字节不抄。 这个内部类型叫 ConsString(拼接串),对外表现得和普通字符串一模一样, 但只额外占用大约 24–32 字节21。

这不是空谈,书里用 V8 的隐藏调试语法 %DebugPrint()(启动 Node 时加 --allow-natives-syntax 参数才能用)当场验证22:

  > let hello = "hello"
  > %DebugPrint(hello)
  - type: INTERNALIZED_ONE_BYTE_STRING_TYPE     ← 普通小串:内部化的单字节串

  > let m = "knock knock!"; let n = "who's there?"
  > %DebugPrint(m + n)
  - type: CONS_ONE_BYTE_STRING_TYPE             ← 拼接结果:只存两个指针
  - instance size: 32

  图说:拼接的“结果”此刻还没有内容,内容还在两个原串里;要用的时候才组装。

注意第二段:两个原串还在,各自是 INTERNALIZED_ONE_BYTE_STRING_TYPE (内部化串,马上讲),新串只是指向它们23。 用这套机制,拼两个字符串是常数时间——不管两边多大,代价都是存两个指针24。

把 3.2 节的表格生成器改成「数组攒碎片 + 最后 join('')」, V8 就会把 join 的结果建成一棵 ConsString 树(引用树),不做无谓复制。 账立刻改观:时间从平方级变线性级(线性:代价随规模等比例涨,不再翻倍翻四倍);真实数据下成品仍是约 2.5 MB, 但内存峰值从 12 MB 降到 2.7 MB 左右——省了近一半内存25。

3.6 但是引擎也会变卦:什么时候 V8 不给你 ConsString

V8 不是见拼接就存指针。如果你拼的是两个临时小串——"hello " + "world" 这种——%DebugPrint 显示类型是 SEQ_ONE_BYTE_STRING_TYPE: 一个顺序存储的普通串,内容真的复制好了26。

为什么?因为 V8 在赌:马上要有操作(取值、打印、切片)需要直接读内容, 到时候还得展平,不如现在就抄好27。引擎的优化是权衡,不是教条—— 这一点在数字章(04/05)和对象章(10)还会反复出现。

3.7 展平(flattening):债迟早要还,但可以晚点还

ConsString 指针树的账,用的时候是要还的。当某个操作需要连续读内容 (比如按位置取字符),V8 会把整棵树走一遍,把所有内容抄进一块新内存, 替换掉指针结构——这个操作叫展平(flattening),产物是 SEQ 字符串28。 展平是惰性的:你不去碰内容,它就不发生;而且一旦发生,就是全量复制一份, 大字符串还会因此掉进 Large Object Space,被更久地留在内存里29。

书里有个很刁钻的实例:把拼好的页面写进文件。Node.js 的 createWriteStream 写文件走的是 Node 内部的 C++ 代码,不是标准 JavaScript 的字符串读取操作—— V8 根本不知道 Node 要用这个字符串,所以不会主动展平; Node 只好请 V8 把 ConsString 转成 UTF-8 字节再落盘,而实测 用 ConsString 直接写,比先展平再写要慢30。

作者的基准:两种写法各跑 10,000 次、每次写含 10,000 个字符的大串, 未展平版 817.6 毫秒,先展平版 782.5 毫秒,差 4.31%31。 数字不大,但方向明确:ConsString 是构造期的神优化,使用期却可能是负担。

那怎么主动展平?书里抛出一个看起来莫名其妙的函数32:

function flatstr(s) {
  s | 0;      // 这一行是故意 的:位运算会触发 V8 展平字符串
  return s;
}

s | 0 把字符串拖进位运算,V8 为了算这个,先把内容读出来——顺手就展平了。 这个技巧来自作者同事 David Mark Clemens 发布的 npm 包 flatstr33。 但书里马上泼了冷水(这是原书的态度,值得尊敬):别依赖这种技巧—— V8 的优化随时变,今天触发展平的操作,下个版本可能就换了;要紧的不是 「怎么触发展平」,而是「展平这件事存在、且有成本」34。

3.8 其余的串型:ThinString、SlicedString、ExternalString、InternalizedString

ConsString 之外,V8 还有一抽屉专用类型,书里交代了各自的存在理由35:

类型存什么什么时候出现
InternalizedString(内部化串)全局唯一的一份内容小串直接量、属性名、Symbol 的描述——相同的串全进程只存一份,比较变成指针比较
ThinString指向另一个串的单指针发现两个串内容相同时,把其中一个降级成指针,省一份副本
SlicedString母串指针 + 偏移 + 长度每次你调用 slice():切 片不复制,「cde」只是「abcdef 从 2 开始长 3」
ExternalString指向 V8 堆外的内存宿主(Node.js)把字符串数据放在自己管的内存里,堆内只留一个指针
OneByte/TwoByte内容本体内容全是 Latin1 → 每字符 1 字节;有非 Latin1 → 每字符 2 字节36

单字节/双字节的自动选择值得单独说一句:规范要求每元素 16 位, 但 V8 发现你的串全是 ASCII,就实际按每字符 1 字节存,对外仍装作 16 位序列—— 规范和实现各说各话,互不打脸37。

书里的总评很符合这一章的调子:这些类型你都不该需要去想, V8 内部全包了;但你要知道它们存在,因为「用对模式」的本质 就是让 V8 的这些机关有机会启动38。

3.9 时效警告:这套优化是有保质期的

原书用一段话给整章踩了刹车:V8 的优化逐版本在变——今天的妙招, 几年后可能失效,甚至反过来拖后腿。十年前 V8 用的是另一套编译优化策略, Node.js 开发者们围着那套策略调优了无数小时,V8 换策略那天,这些优化代码 全部作废39。所以本章(以及 04、05、10 章)的所有具体建议,都要加上 「以 V8 的当前实现为准」的脚注——机制层面的道理(不可变、共享、惰性) 比具体技巧长寿得多。

3.10 本章的章末总结:原书十二条对照

原书第 2 章结尾给了十二条总结,逐条对应本章各节,照录作核对单40:

  1. JavaScript 字符串 = 一串 16 位无符号整数,可能是、也可能不是合法 UTF-16 文本;
  2. 编码是「符号 ↔ 数字」的映射:ASCII 管 128 个,Unicode 已定义 15 万+;
  3. UTF-8 变长 1–4 字节省传输,UTF-16 每字符 2–4 字节是语言的内部表示;
  4. 代理对用两个 16 位值,合表编号超过 65,535 的字符;
  5. ByteString 装原始字节、DOMString 允许坏序列、USVString 保证合法;
  6. 编码不匹配出真 bug:系统假设 UTF-8,撞上 Shift-JIS 这类老编码;
  7. 字符串不可变 ⇒ V8 的 ConsString 拼接优化成为可能;
  8. normalize() 解决「看起来一样」的比较问题;
  9. 正则处理非 ASCII 必须带 u 旗标;
  10. V8 内部有六种串型:OneByte/TwoByte/Cons/Sliced/External/Thin;
  11. ConsString 免复制,把拼接的内存从平方级压成线性;
  12. 字符串长度有实际上限(32 位约 256 MB,64 位约 512 MB)。

十二条的前六条属于 02 章,后六条属于本章——一张清单,两章的桥。 如果读完发现哪一条对不上自己在本章看到的机制,回到对应小节重读即可。

3.3b %DebugPrint 的输出里还有什么

3.5 节用 %DebugPrint 验 证了 ConsString,那一大段诊断输出里还有两个信息值得认41:

  • - instance size: variable(普通串)vs - instance size: 32(ConsString): 实例大小直接可见——「24–32 字节额外开销」的 32 就来自这里;
  • - type: INTERNALIZED_ONE_BYTE_STRING_TYPE:普通小串默认内部化, 即相同内容的串全进程只留一份,相等比较退化成指针比较42。

顺带重申那条警告:%DebugPrint 属于 V8 的私有调试语法, 不是公开支持的机制,版本间输出格式会变——教学可以,生产禁用43。 这也是为什么书里所有用它做的实验都标注了 Node 版本: 你现在复跑,输出可能已有出入,但类型名和量级仍然可信。

3.11 三个反模式的总复盘:同一个病的三种发烧

回看 3.2 的三个反模式,它们其实是同一个病在不同部位的三种发烧:

  病根:把「构造期的临时状态」当成了「可以随便挥霍的资源」
    ├─ 循环 += :临时状态是「累积中的大串」,复制随 n² 涨
    ├─ 递归拼串:临时状态是「各层返回的中串」,每层再复制一遍
    └─ 无界缓存 :临时状态被「优化」永久化,峰值变常驻

  统一处方:让构造期的中间产物尽量小、尽量短命
    ├─ 攒碎片 + join:中间产物是一堆小串,树状引用不复制
    ├─ 递归也攒碎片:每层只 push,不拼接
    └─ 缓存有界 + 有出口:存之前想好什么时候扔

这个处方背后的机制只有一个:V8 的优化(ConsString、SlicedString、 内部化)全部偏爱「小而多、结构稳定」的串;而「大而不停重写」的串, 每种优化都使不上。03 章讲的不是三个坏习惯,是一个选材原则—— 你交给引擎的串的「形状」,决定了引擎能为你做什么。

最后把这一章也挂回全书的主线:字符串的「表示自由」是实现层的礼物 (规范只管「一串 16 位整数」,怎么存全由引擎),它的性能收益 (拼接免复制、切片免复制、重复内容自动去重)是 V8 给你的; 而作为交换,你交出去的是「构造方式的知情权」—— 同样的 a + b,引擎可以给你指针、也可以给你副本, 取决于它此刻的判断。信任引擎,但给它一个能优化的输入, 是这一章全部建议的共同句式。

作者的判断与证据

有证据的判断:

  • ConsString 的存在与行为,用 %DebugPrint 的输出直接证明(类型名、实例大小 32 字节)44;
  • 「join 版省 46% 内存」「写文件差 4.31%」有书中实测数字支撑,虽然作者也提醒 不同机器结果不同45。

作者的主张:

  • 「12 MB 峰值」「五倍」这类反模式账单,标的是「naïve implementation(朴素实现)」 下的推算,不是实测——V8 有 ConsString 兜底,真实代码里的差距通常没那么惨, 但方向不变46。
  • 「永远别用 Boxed String」「别依赖 flatstr」属于作者的最佳实践主张。

判断(我们的,不是书里的): 本章是「引擎的优化都建立在规律上」这条总原则的 第一次完整展示:ConsString 赌「拼接多、访问少」,展平赌「访问前值得一次复制」。 理解这个「对赌」结构,比记住任何单条技巧都保值——它能迁移到 V8 之后的版本、 以及其他动态语言的运行时上。 如果错,会错在: 如果未来的引擎对无规律代码也能保持接近线性的性能 (比如全面换用更强的静态分析或新的内存布局),「配合引擎规律」的建议就会从 性能必需降级为风格偏好——但「优化会随版本漂移」这个警告本身不会失效。

边界与局限

  • 内存管理只开了个头(堆/页/大对象区),垃圾回收的机制细节承诺在「后面的章节」—— MEAP 里还没有。
  • %DebugPrint 是 V8 私有调试接口(对外暴露的功能入口),不能用于生产,版本间输出格式会变47。
  • 性能数字全部绑死特定 Node/V8 版本与机器,只宜看比例,不宜抄绝对值。
  • 大对象 128 KB 阈值(分界线数值)是 V8 实现细节,不是规范,别写进任何跨引擎的假设。

可带走的

  1. 字符串不可变:一切「修改」都是新建;这是后面所有优化的地基。
  2. 循环里 += 拼串是平方级:累计复制字节 = 行长 × n(n+1)/2。
  3. 攒数组 + join('') 把平方变线性,内存峰值可近砍半。
  4. 缓存必须 有界、有出口;「失败条目清不掉」这类 bug 会安静地吃掉全部内存。
  5. V8 的存储世界观:堆 → 页(256 KB)→ 大对象区(128 KB 以上、回收时不挪)。
  6. ConsString = 拼接不复制只存指针;slice() = 存偏移;重复串 = 一个降级成指针。
  7. 展平是惰性的、全量的;构造期省下的复制,使用期可能连本还。
  8. 单字节/双字节自动切换:全 ASCII 的串,实际只占一半内存。
  9. 所有「怎么写更快」的技巧都有保质期;机制理解比技巧长寿。

原文地图

主题原书章原文位置
模板生成场景与三个反模式2.2.1 Setting the stage: pathologically bad template generationtext/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:13(搜「Template generation is the process」)
不可变与静默失败同上text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:143(搜「immutable」)
+= 的平方级账同上text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:117(搜「quadratic time complexity」)
2.4 MB 成品 vs 12 MB 峰值同上text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:239(搜「peak of up to 12 MB」)
递归菜单 244 KB vs 1.2 MB同上text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:298(搜「244 KB」)
15% 失败率缓存事故同上text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:344(搜「every 30 seconds」)
堆、页、大对象区2.2.2 String memorytext/11-ch02-02-2-2-2-string-memory.txt:19(搜「large storage warehouse」) · :22(搜「256 KB」) · :30(搜「128 KB」)
512 MB 实际长度上限同上text/11-ch02-02-2-2-2-string-memory.txt:49(搜「256 MB」)
大对象不被挪动、碎片化同上text/11-ch02-02-2-2-2-string-memory.txt:65(搜「never moved」)
六种内部串型总表2.2.3 V8’s multiple string typestext/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:33(搜「V8’s various string types」)
ConsString 只存指针同上text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:152(搜「24-32 additional bytes」)
%DebugPrint 实测 CONS 类型同上text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:254(搜「CONS_ONE_BYTE_STRING_TYPE」)
join 版线性化与省 46%同上text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:322(搜「46% reduction」)
小临时串反而 SEQ同上text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:334(搜「does not always produce」)
展平机制与惰性同上text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:359(搜「walk the structure」) · :414(搜「lazy and can be expensive」)
写文件时 ConsString 更慢同上text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:435(搜「quite a bit slower」)
flatstr 技巧与其警告同上text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:529(搜「strike you as rather odd」)
Thin/Sliced/External同上text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:571(搜「ThinString」) · :574(搜「SlicedString」) · :579(搜「externalized string」)
优化会过时的警告同上text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:591(搜「change from one version to the next」)
章末十二条总结2.2.3 V8’s multiple string typestext/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:603(搜「sequences of 16-bit」)
%DebugPrint 输出细节同上text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:219(搜「INTERNALIZED_ONE_BYTE_STRING_TYPE」) · :255(搜「instance size: 32」)

Footnotes

  1. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 143 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:143,搜「immutable」)。代码示例:str[0] = 'H' 之后 str 仍是 'hello'。 ↩

  2. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 165–168 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:167,搜「10,000 intermediate strings」)。 ↩

  3. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 158 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:158,搜「enables V8’s various optimizations」)。 ↩

  4. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 174 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:174,搜「Array.join() or template literals」)。 ↩

  5. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 101–105 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:101,搜「Array(25000)」)与第 130 段(搜「1.9 MB」)。 ↩

  6. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 117 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:117,搜「increases as the square」)。原书写作「1 + 2 + 3 + … + n」,转码中公式尾部丢失,意指 n(n+1)/2。 ↩

  7. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 239 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:239,搜「peak of up to 12 MB」)。 ↩

  8. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 261–298 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:261,搜「generateNestedMenu」)。 ↩

  9. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 298 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:298,搜「could exceed 1.2 MB」)。 ↩

  10. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 316–338 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:327,搜「getCachedResult」;:338 搜「never removed」)。 ↩

  11. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 344 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:344,搜「15%」)。「跑了一年」与「每 30 秒清空」都在这一段。 ↩

  12. 出处:「2.2.2 String memory」第 13 段(text/11-ch02-02-2-2-2-string-memory.txt:13,搜「heap, a page, and the Large Object Space」)。 ↩

  13. 出处:「2.2.2 String memory」第 19 段(text/11-ch02-02-2-2-2-string-memory.txt:19,搜「large storage warehouse」)。 ↩

  14. 出处:「2.2.2 String memory」第 22 段(text/11-ch02-02-2-2-2-string-memory.txt:22,搜「256 KB」)。 ↩

  15. 出处:「2.2.2 String memory」第 25 段(text/11-ch02-02-2-2-2-string-memory.txt:25,搜「special region」)。 ↩

  16. 出处:「2.2.2 String memory」第 30 段(text/11-ch02-02-2-2-2-string-memory.txt:30,搜「less than 128 KB」)。 ↩

  17. 出处:「2.2.2 String memory」第 65 段(text/11-ch02-02-2-2-2-string-memory.txt:65,搜「never moved during garbage collection」)。「碎片化和持续高内存占用」在同段。 ↩

  18. 出处:「2.2.2 String memory」第 65 段(text/11-ch02-02-2-2-2-string-memory.txt:65,搜「the way you build and store strings」)。 ↩

  19. 出处:「2.2.2 String memory」第 49 段与第 49 段(text/11-ch02-02-2-2-2-string-memory.txt:49,搜「256 MB」;:52 搜「512 MB」)。规范上限 2^53-1 见同文件第 43 段(搜「9 petabytes」)。 ↩

  20. 出处:「2.2.3 V8’s multiple string types」第 133 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:133,搜「in sequence」)。 ↩

  21. 出处:「2.2.3 V8’s multiple string types」第 152 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:152,搜「24-32 additional bytes」)。 ↩

  22. 出处:「2.2.3 V8’s multiple string types」第 170–198 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:170,搜「native syntax」;:183 搜「educational purposes only」)。生产禁用的警告在同段。 ↩

  23. 出处:「2.2.3 V8’s multiple string types」第 268 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:268,搜「CONS_ONE_BYTE_STRING_TYPE」)。 ↩

  24. 出处:「2.2.3 V8’s multiple string types」第 281 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:281,搜「constant time」)。 ↩

  25. 出处:「2.2.3 V8’s multiple string types」第 322 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:322,搜「46% reduction」)。改造代码清单 2.10 在同文件第 291–303 段。 ↩

  26. 出处:「2.2.3 V8’s multiple string types」第 334–345 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:334,搜「does not always produce」;:345 搜「SEQ_ONE_BYTE_STRING_TYPE」)。 ↩

  27. 出处:「2.2.3 V8’s multiple string types」第 352 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:352,搜「immediate access」)。 ↩

  28. 出处:「2.2.3 V8’s multiple string types」第 359 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:359,搜「walk the structure」)。 ↩

  29. 出处:「2.2.3 V8’s multiple string types」第 414 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:414,搜「Flattening is lazy and can be expensive」)。 ↩

  30. 出处:「2.2.3 V8’s multiple string types」第 435 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:435,搜「quite a bit slower」)。 ↩

  31. 出处:「2.2.3 V8’s multiple string types」第 502–516 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:508,搜「817.644487」;:516 搜「4.31%」)。「不同机器结果不同」见第 502 段(搜「different machines」)。 ↩

  32. 出处:「2.2.3 V8’s multiple string types」第 537 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:537,搜「s | 0」)。 ↩

  33. 出处:「2.2.3 V8’s multiple string types」第 545 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:545,搜「David Mark Clemens」)。 ↩

  34. 出处:「2.2.3 V8’s multiple string types」第 545 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:545,搜「change all the time」)。 ↩

  35. 出处:「2.2.3 V8’s multiple string types」第 565–585 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:565,搜「not the only internal alternative」)。 ↩

  36. 出处:「2.2.3 V8’s multiple string types」第 13–21 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:13,搜「One-byte strings」;:21 搜「automatically choose」)。 ↩

  37. 出处:「2.2.3 V8’s multiple string types」第 27 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:27,搜「look and act like a normal 16-bit」)。 ↩

  38. 出处:「2.2.3 V8’s multiple string types」第 585 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:585,搜「shouldn’t need to think about them」)。 ↩

  39. 出处:「2.2.3 V8’s multiple string types」第 591 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:591,搜「A decade ago」)。 ↩

  40. 出处:「2.2.3 V8’s multiple string types」第 603–636 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:603,搜「sequences of 16-bit」):十二条总结依次列出,前后六条的归章是我们的整理。 ↩

  41. 出处:「2.2.3 V8’s multiple string types」第 211–233 段与第 250–257 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:220,搜「instance size: variable」;:255 搜「instance size: 32」)。 ↩

  42. 出处:「2.2.3 V8’s multiple string types」第 239 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:239,搜「Most of your simple strings」):「你的大多数简单串都会以 INTERNALIZED_ONE_BYTE_STRING_TYPE 创建。」 ↩

  43. 出处:「2.2.3 V8’s multiple string types」第 183 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:183,搜「Don’t use native syntax」)。 ↩

  44. 出处:「2.2.3 V8’s multiple string types」第 246–268 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:246,搜「knock knock」)。 ↩

  45. 出处:「2.2.3 V8’s multiple string types」第 502 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:502,搜「different machines will produce different results」)。 ↩

  46. 出处:「2.2.1 Setting the stage: pathologically bad template generation」第 239 段与第 239 段(text/10-ch02-02-2-2-1-setting-the-stage-pathologically-bad-templ.txt:239,搜「in a na ve implementation」;:298 同)。「naïve」的转码丢失见原文拼写。 ↩

  47. 出处:「2.2.3 V8’s multiple string types」第 183 段(text/12-ch02-02-2-2-3-v8-s-multiple-string-types.txt:183,搜「Don’t use native syntax」)。 ↩

On this page