内存分析101 - acelan86/chromeDevTools GitHub Wiki

[原文地址]https://developers.google.com/chrome-developer-tools/docs/memory-analysis-101?hl=zh-CN

这是一个关于内存分析的介绍。这里描述的术语和概念被用于堆审查器UI和相对应的文档。你需要熟悉他们以便更高效的使用工具。

常见术语

这部分描述了在内存分析中用到的一些常见术语,适用于不同语言的各种内存分析工具。如果你已经体验过,如,Java或者.Net内存分析器,你会有更多的机会熟悉他们。

对象大小(Object Sizes)

内存以两种方式被一个对象保持:通过对象自身直接保持,或者隐式的通过保持其他对象的引用来保持,防止他们被垃圾回收器(简称GC)自动释放。

通过对象自身保持的内存大小称为shallow size。典型的Javascript对象保留了一些内存用于自身描述和存储立即值。

通常,只有array和string拥有实际意义上的shallow size。但是,strings通常再渲染内存中有他们的主要存储器,只在Javascript堆中暴露一个小小的包装对象。

不但如此,甚至通过阻止其他对象被垃圾回收处理过程自动释放,一个小的对象可以直接保持一大块的内存空间。当对象自身被删除时,这块内存大小将被释放,因为它所依赖的对象从GC根上变得无法到达,称为retained size。

保持路径(Retaining Paths)

堆是互相联系对象的一个网络。在数学世界中,这个结构叫做图。图是由一些节点通过边连接构成的。节点和边都有标签:节点(对象)使用创建他们的构造函数的名字来标注,边使用属性的名字来标注。

var a = new A(),
    b = new B();
a.prop = b;

这段代码的堆像下面这样:

从一个对象到达另一个对象按顺序遍历所需的边序列称为路径。通常,我们只对最短路径感兴趣,例如,不两次经过同一个节点的路径。

我们称那些从GC根到某个对象的路径为保持路径。如果没有这样的路径,我们称对象无法到达并且在垃圾回收过程中会被清除。

支配者(Dominators)

一个对象A的支配者是存在于从根到对象A的最短路径中的对象。也就是说,一旦支配对象从堆上被移除(并且所有它的引用被切断)。对象A将变得从GC根上无法到达,并且会被释放。

支配对象包含一个树结构,因为每一个对象有一个确切的支配者。一个对象的支配者可能缺少对它支配的对象的直接引用,也就是,支配树不是图的生成树。

类似集合对象可能保持很大一块内存,当他们支配其他对象的时候。树中这样的节点称为积累点。

V8特性

这部分我们描述了只跟V8 Javascript虚拟机相关的一些内存话题。阅读它们可以帮助你理解为什么堆快照看起来是这样的。

Javascript对象表示法

数字可能被表现成31位立即整型值(称为小整型,或者简称为SMI),或者堆对象(称为堆数字)。后者被用于不能适应SMI格式的时候,比如双精度数字,或者用于一个值需要被装箱的时候,例如,给它设置属性。

字符串内容可以被存储在虚拟堆中,或者体现在渲染内存中。从Web中接收到的内容(例如,scripts源代码)不会被复制到虚拟堆上,取而代之的是,创建一个包装对象来访问外部存储。

当两个字符串连接的时候,他们的内容被分开初始化存储,而只是从逻辑上被连接,使用一个叫做cons string的东西。cons string内容的连接只在需要的时候被处理。例如,当一个连接字符串的字串被构造的时候。

在V8虚拟机中,数组被广泛的应用于存储大块数据。字典(key-value对的集合)通过arrays来备用。因此,数组是Javascript对象基本的构建块。一个典型的javascript对象具有两个数组:一个用于存储命名属性,另外一个用于存储数值元素。在属性的数目非常小的情况下,他们可以被存储在javascript对象自身内部。

一个map对象描述对象种类和它的布局。例如,maps被用于描述隐含对象层次,就像这里描述的。

对象组(Object Groups)(这段翻译有点奇怪)

每一个本地对象组由和其他对象保持公共引用的对象构成。考虑DOM子树的例子,那些每一个节点有一个到它的父节点并且连接到下一个子节点和下一个兄弟节点的链接,因此构成一个连接图。注意本地对象不会体现在Javascript堆中 - 也就是为什么他们是0大小。代替的是,包装对象被创建。每一个包装对象持有一个到对用的本地对象的引用,为了直接运行它。在它自己的轮中,一个对象组持有包装对象。但是,这没有创建一个不能收集的循环,GC足够聪明以释放那些包装器不再引用的对象组。单忘记释放单独的包装器将保持整个组和关联包装器。