Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# xfe
<<<<<<< HEAD
前端小分队
=======
前端知识拯救计划

## 贡献文章方法
Expand Down Expand Up @@ -49,3 +52,4 @@ author: acelan
```text
![altText](a.png)
```
>>>>>>> 06e8c4e3ab4b9a1ce672c75f9eafff7939091e61
27 changes: 27 additions & 0 deletions source/_posts/fix-memory-problems.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
title: 处理内存问题
date: 2016-05-04 14:16:49
tags: [chrome, DevTools, profile]
author: jiliang

---


原文出自: https://developers.google.com/web/tools/chrome-devtools/profile/memory-problems/?hl=en
原文作者: Meggin Kearney

---


程序临时使用内存后不能释放内存时,会发生内存丢失。注意内存泄漏,溢出,和强制垃圾回收。

---

现在的JavaScript引擎在许多情况下可以高度自动化地清理我们的代码产生的垃圾。也就是说,它们也只能做到这样,我们的应用程序仍然容易发生逻辑错误造成的内存泄漏。用这些工具来找出你的瓶颈,记住,不要猜测,去测试。

---

### 主题
- [内存诊断]()
- [内存术语]()
- [如何记录堆快照]()
- [如何使用分配分析器工具]()
152 changes: 152 additions & 0 deletions source/_posts/memory-diagnosis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
title: 内存诊断
date: 2016-05-04 15:26:28
tags: [memory, DevTools]
author: jiliang

---

原文出自: https://developers.google.com/web/tools/chrome-devtools/profile/memory-problems/?memory-diagnosis?hl=en
原文作者: Meggin Kearney

---

内存泄漏是可用计算机内存的逐渐丢失,发生在程序临时使用内存后不能释放内存时。当你怀疑自己造成内存泄漏时,按照以下目录表中列出的调查步骤提纲测试。

> 目录
>>
[检查页面是否用了太多内存]()
[发现没有被垃圾回收清理的对象]()
[减少内存泄漏的因素]()
[限定垃圾回收频率]()
[内存分析资源]()

---

> TL;DR(Too Long; Don,t Read)
>>
通过Chrome Task Manager监控内存条目快速查看页面是否消耗太多内存。
使用[Chrome Develop Timeline](https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/timeline-tool)中的momory view确认内存使用是否在增长。
使用Chrome DevTools heap profiler确认已分离的节点是否仍然占用内存。
注意频繁的垃圾收集和垃圾收集暂停,二者都会影响性能。

---

### 检查页面是否用了太多内存

检查一个页面是否使用太多内存,首先要做的就是确认你怀疑的一系列行为是否正在泄漏内存。这可能是网址导航的任何事件,鼠标悬浮,点击,或者其他的某些随着时间推移影响性能的页面交互。
一旦你怀疑内存性能问题,当你第一次注意到你的页面长时间使用后变慢时,使用[Chromr Develop Timeline](https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/timeline-tool)诊断过度内存使用。

> 注意
>>
第一次听说内存管理?开始查看[内存术语](https://developers.google.com/web/tools/chrome-devtools/profile/memory-problems/memory-101)基础。

#### 使用Chrome任务管理器监控内存

用Chrome任务管理器监控一个页面的活动内存使用。
从Chrome菜单进入任务管理器>Tools>Task Manager(快捷键Shift+Esc)。
打开后,右键点击条目的头部区域,能看到JavaScript memory选项。运行的事件可能用太多内存,监控活动内存使用的变化。

![task-manager.npg](task-manager.png)

#### 使用memory视图确认内存使用是否在增加

要诊断是否是内存问题,要转到Timeline面板Memory视图。点击Record按钮,与你的应用程序进行交互,重复任何你认为可能导致泄漏的步骤。停止记录。
你看到的图表显示你的应用程序分配到的内存。如果它恰巧在这段时间消耗大量增长(而且没有下降),这就是内存泄漏的迹象。
![normal-sawtooth.png](normal-sawtooth.png)

正常的应用程序形状应该更像一个锯齿状曲线,因为垃圾回收器的介入使内存分配后释放。不必担心这个——运行JavaScript总会有额外消耗,即便是一个空的`requestAnimationFrame`也会产生这种锯齿状,这无法避免。
只要锯齿不是尖锐的,那就说明很多内存被分配了,另一方面也产生了很多垃圾。你必须注意曲线陡峭度增加的比例。
![steep-sawtooth.png](steep-sawtooth.png)

Memory视图中的DOM node counter,Document counter 和Event listener count在诊断过程中会很有用。DOM节点使用本地,不直接影响JavaScript内存图。
一旦你怀疑有内存泄漏,用heap profiler和object allocation tracer找泄漏的原因。

**例子:**尝试这个[内存增长](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example1.html)的例子,你可以练习高效使用Timeline memory模式。


### 发现没有被垃圾回收机制清理的对象

用Profiles面板的Chrome DevTools Heap profiler发现没有被垃圾回收机制清理的对象。

Heap profiler通过页面的JavaScript对象和相关DOM节点显示内存分配。这有助于发现另外的不可见的泄漏,这些泄漏的发生是由于被遗忘的分离的DOM子树的游离。

用heap profiler能获取JS heap snapshots,分析内存图表,比较snapshots,探测DOM泄漏(详见[How to Record Heap Snapshots](https://developers.google.com/web/tools/chrome-devtools/profile/memory-problems/heap-snapshots))。
在constructor和retained视窗有很多数据。以最短距离保留的对象通常是导致内存泄漏的第一个候选者。内存泄漏侦查从你的DOM树中的第一个保留对象开始,因为保留的对象按到窗口的距离排序。

![first-retained.jpg](first-retained.jpg)

当心你的heap snapshots中的黄色和红色的对象。红色节点(深色背景)没有从JavaScript到他们的直接引用,但是他们却是活动的,因为他们是分离的DOM树的一部分。也可能是一个引用自JavaScript树中的节点(可能是作为一个闭包或者变量),但恰巧阻止了整个DOM树被垃圾回收。

![red-yellow-objects.jpg](red-yellow-objects.jpg)

然而黄色节点(黄色背景)是真有JavaScript引用。在同一个分离的DOM树中寻找黄色节点,定位JavaScript引用。应该有一个从DOM window到元素的属性链(比如window.foo.bar[2].baz)。

观察这个动画,理解分离的节点从哪里融入整个图片:

![detached-nodes.gif](detached-nodes.gif)

**例子:**尝试这个[分离节点](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example4.html)的例子,你能在Timeline中看到节点演变过程然后获得heap snapshots找到分离节点。

###减少内存泄漏的因素
用object allocation tracker通过看JS对象实时分配减少内存泄漏的原因。**object tracker**结合了heap profiler的详细快照信息和Timeline面板增加的更新和跟踪。

跟踪对象的堆分配包括开始一个记录,展示一系列行为,停止记录分析。object tracker在整个记录期间定时获得heap snapshots(如每50ms一次),在记录末尾还有一次最后快照。堆分配配置资料展示对象在哪里创建,确认保留路径:

![allocation-tracker.png](allocation-tracker.png)

在[How to Use the Allocation Profiler Tool](https://developers.google.com/web/tools/chrome-devtools/profile/memory-problems/allocation-profiler)学习怎样使用这个工具。


### 限定垃圾回收频率

垃圾回收器(比如在V8引擎中的这个)需要能在应用程序中定位活动的对象,也能定位死的对象(垃圾)和不能获得的对象。如果在做频繁的垃圾回收,你可能分配内存太过频繁了。

也要注意有趣的垃圾回收暂停。如果垃圾回收(GC)丢失了任何在你的JavaScript中由于逻辑错误导致的死对象,这些对象的内存消耗不能被收回。这种情形会随着时间最终降低你的应用程序的速度(详见[How to Use the Allocation Profiler Tool](https://developers.google.com/web/tools/chrome-devtools/profile/memory-problems/allocation-profiler))。

这经常发生在当你不需要的变量和事件监听器依然被一些代码引用时。维护这些引用时,这些对象不能被垃圾回收机制正确清理。

记得检查并注销包含DOM元素引用的变量,这些变量可能会在你的app的生命周期内更新或者摧毁。检查可能引用其他对象(或者其他的DOM元素)的对象属性。注意可能随时间推移累积的变量缓存。



### 内存分析资源

下面是从增长的内存泄漏DOM节点开始的一系列连续的测试变化的内存的例子:

- [Example 1: 增长的内存](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example1.html)
- [Example 2: 垃圾回收行为](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example2.html)
- [Example 3: 散落的对象](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example3.html)
- [Example 4: 分离的节点](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example4.html)
- [Example 5: 内存和隐藏类](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example5.html)
- [Example 6: 泄漏的DOM节点](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example6.html)
- [Example 7: Eval是魔鬼(几乎总是)](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example7.html)
- [Example 8: 记录对堆分配](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example8.html)
- [Example 9: DOM泄漏比预期的要更大](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example9.html)
- [Example 10: 保留路径](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/demos/memory/example10.html)

从下面的地址可以获得更多的demos:

- [收集散落的对象](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/heap-profiling-summary.html)
- [判定行为清洁](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/heap-profiling-comparison.html)
- [探索堆内容](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/heap-profiling-containment.html)
- [揭秘DOM泄漏](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/heap-profiling-dom-leaks.html)
- [发现累积点](https://github.com/GoogleChrome/devtools-docs/blob/master/docs/heap-profiling-dominators.html)

#### 额外资源

[内存管理大师](https://youtu.be/LaxbdIyBkL0)Addy Osmani给你一个调试内存问题的速成课。[幻灯片展示](https://speakerdeck.com/addyosmani/javascript-memory-management-masterclass)和[样例代码](https://github.com/addyosmani/memory-mysteries)都是可以获得的。
视频地址:https://youtu.be/LaxbdIyBkL0

#### 社区资源
有许多社区成员写的用Chrome DevTools查找和处理web app内存问题的精彩资源。以下是有用的选择:

- [用Chrome DevTools发现和调试内存泄漏](http://slid.es/gruizdevilla/memory)
- [用DevTools进行JavaScript分析](http://coding.smashingmagazine.com/2012/06/12/javascript-profiling-chrome-developer-tools/)
- [Gmail级有效的内存管理](http://www.html5rocks.com/en/tutorials/memory/effectivemanagement/)
- [Chrome Devtools 2013大改版](http://www.html5rocks.com/en/tutorials/developertools/revolutions2013/)
- [用DevTools渲染和内存分析](http://www.slideshare.net/matenadasdi1/google-chrome-devtools-rendering-memory-profiling-on-open-academy-2013)
- [用DevTools Timeline和Profile进行性能优化](http://addyosmani.com/blog/performance-optimisation-with-timeline-profiles/)




Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/_posts/memory-diagnosis/first-retained.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/_posts/memory-diagnosis/steep-sawtooth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/_posts/memory-diagnosis/task-manager.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
150 changes: 150 additions & 0 deletions source/_posts/memory-terminology.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
title: 内存术语
date: 2016-05-05 15:07:30
tags: [DevTools, memory, terminology]
author: jiliang
---


原文出自:https://developers.google.com/web/tools/chrome-devtools/profile/memory-problems/memory-101?hl=en
原文作者:Meggin Kearney

---

本节介绍了用于内存分析的常用术语,和适用于不同语言的很多内存分析工具。

---

这些术语和概念指的是[Chrome DevTools Heap Profiler](https://developers.google.com/web/tools/chrome-devtools/profile/memory-problems/heap-snapshots)。如果你用过Java,.NET或者其他的内存分析器,那么这将是一次复习提升。

> 目录
>> 对象大小
>> 对象保留树
>> 支配者
>> V8特性


##对象大小
把内存当做一个基本类型(如number,string)和对象(含数组)组成的图。它被形象地表示为有很多相互关联的点,如下图:

![thinkgraph.png](thinkgraph.png)

对象占用内存有两种方式:

- 对象自己直接占用。
- 隐式地通过其他对象的引用占用,因而阻止那些对象被垃圾回收机制(简称**GC**)自动处理。

当用DevTools的Heap Profiler(Profiles下用于审查内存问题的工具)时,你可能会发现你正在看一些不同的信息栏。两个突出的是**Shallow Size**和**Retained Size**,这些是什么?

![shallow-retained.png](shallow-retained.png)

###Shallow size

这是对象自己占用的内存大小。

典型的Javascript对象保留一些内存来存储它的描述和值。一般情况下,只有数组和字符串会有明显的shallow size。然而,在渲染器内存中,字符串和外部数组经常有他们的主仓库,在JavaScript堆上只暴露一个小的封装对象。

渲染内存是检查渲染页面的进程的所有内存:本地内存+当前页JS堆内存+页面开始后的所有专用JS堆内存。然而,小对象也可以通过阻止其他对象被垃圾回收进程自动处理间接占用大量内存。

###Retained size

这是被删除的对象释放的内存大小,这个对象伴随着它所依赖的不能从**GC roots**得到的对象被删除时释放内存。

**GC roots**由(本地或全局的)引用本地代码到V8外的JavaScript对象时创建的handles组成。这些handles会在**GC roots** > **Handle scope**和**GC roots** > **Global handles**下的heap snapshot找到。只在文档中描述handles而不深入浏览器实现细节会很迷惑。GC roots和handles都是比不用担心的。

大部分内部GC roots用户并不关心。从应用的角度讲有以下几种roots:

- Window全局对象(在每个iframe里)。在heap snapshots中有一个distance区域,展示的是在到window的最短保留路径上属性引用的数量。
- 通过遍历文档可以获得由所有原生DOM节点组成的文档DOM树。并不是所有的节点都有JS封装,但是如果有的话,文档活动的时候这些节点也活动。
- 有时对象会被调试环境和DevTools控制台保留(例如控制台赋值之后)。清空控制台关闭调试器的断点后再创建heap snapshots。

内存图从根开始,这个根可能是浏览器的window对象或者Node.js模块的全局对象。你不能控根对象怎样进行垃圾回收。

![dontcontrol.png](dontcontrol.png)

不能达到根节点的节点被作为垃圾回收。

> 注意
>> Shallow和Retained size都用字节表示。

##对象保留树

堆是一个相互连接的对象组成的网络。在数学上,这种结构叫图或者记忆图。图由被边连接的节点构成,边和节点相互对应。

- **节点**(或者说对象)用构造函数的名字指定,构造函数用来创造节点。
- **边**用属性的名字指定。

学习[怎样用Heap Profiler记录profile](https://developers.google.com/web/tools/chrome-devtools/profile/memory-problems/heap-snapshots)。从Heap Profiler记录下看到的明显的东西包括距离:到GC root的距离。如果几乎所有相同类型的对象距离也一样,其他的少量对象距离比较大,那就需要探查一下了。

![root.png](root.png)

##支配者

支配者对象由一个树形结构组成,因为每个对象都有一个支配者。一个对象的支配者可能没有直接引用它所支配的对象;也就是说,支配者的树不是图的生成树。

在下图中:

- 节点1支配节点2
- 节点2支配节点3,4,6
- 节点3支配节点5
- 节点5支配节点8
- 节点6支配节点7

![dominatorsspanning.png](dominatorsspanning.png)

在下面的例子中,节点#3是#10的支配者,但是#7存在于每个从GC到#10的简单路径中。因此,如果B对象B存在于每个从根到对象A的简单路径中,那么B是A的支配者。

![dominators.gif](dominators.gif)

##V8特性

分析内存时,理解为什么用特定的方法看heap snapshots是很有帮助的。本节描述V8 JavaScript虚拟机(V8 VM或VM)内存相关的问题。

###JavaScript对象表示
有三种基本类型:

- Numbers(如3.14159...)
- Booleans(true,false)
- Strings(如"Werner Heisenberg")

他们不能引用其他的值,他们是叶子节点或者终端节点。

**Numbers**可以被存储为:

- 一个被叫做**小整数**(SMIs)的31位整数值,或者
- 堆对象,称作**heap numbers**。Heap numbers是用来存储不适合SMI格式的值,比如doubles,或者当一个值需要被封装的时候,比如给对象设置属性时。

**Strings**可以被存储在:

- **Vm堆**,或者
- **渲染内存**的外部。一个封装对象被创建和用于访问外部存储,如脚本源和从Web接收的 其他内容存储的位置,而不是复制到VM堆。

新的JavaScript对象的内存由一个专用的JavaScript堆(或者VM堆)分配。这些对象被V8的垃圾回收器管理,因此,只要有一个强引用指向他们,他们就会一直存活。

**内置对象**不在JavaScript堆中。内置对象,不同于堆对象,整个生命周期都不被V8垃圾回收机制管理,只能被JavaScript用它的封装对象访问。

**Cons string**是由多对字符串存储后连接组成的,是一系列相互关联的事物的结果。只在需要时发生*cons string*内容的连接。当一个连接的字符串的子字符串需要被创建时生成一个实例。

例如,如果你连接**a**和**b**,你得到一个字符串(a,b)代表连接结果。如果你再把d连接到这个结果上,你会得到另一个cons string((a,b),d)。

**数组**——是带有数字键值的对象。数组在V8 VM中被广泛用于存储大量数据。键值对集合像数组支持的字典一样使用。

典型的JavaScript对象可以用两种数组类型之一存储:

- 命名属性
- 数字元素

很少数的属性存在JavaScript对象内部。

**Map**——描述对象自身和它的布局的对象。例如,maps用来描述[快速属性访问](https://developers.google.com/v8/design.html#prop_access)的隐式对象层次。

###对象组

内置对象组由占用相同引用的对象组成。想一下,例如,一个DOM子树每一个节点有一个到父节点的连接和多个到下一个子节点及其兄弟节点的连接,这样就形成了一个连通图。注意,内置对象不在JavaScript堆中——所以他们的大小是零。创建的封装的对象则相反。

封装的对象占用与内置对象相对应的引用,用于对它重定向命令。对象组最终占据封装对象。然而这并不能创建一个不可回收的循环,因为GC很快就能释放封装没有被引用的对象组。但是,如果忘记释放一个的封装,那么全部组和相关的封装都将被占用。






Binary file added source/_posts/memory-terminology/dominators.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/_posts/memory-terminology/dontcontrol.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/_posts/memory-terminology/root.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added source/_posts/memory-terminology/thinkgraph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading