参考: http://slides.com/gruizdevilla/memory
内存是一张图
- 原始类型,只能作为叶子。原始类型不能引用其他类型
- 数字
- 字符串
- 布尔值
- 除了原始类型之外,其他类型都是对象,其实就是键值对
- 数组是一种特殊对象,它的键是连续的数字
内存从根开始
- 在浏览器中,根对象是window
- 在nodejs中,根对象是global
- 任何从根无法到达的对象,都会被GC回收,例如下图的节点9和10
- 根节点的GC是无法控制的

路径
- 从根节点开始到特定对象的路径,如下图的1-2-4-6-8

支配项
- 每个对象有且仅有一个支配项,支配项对对象可能不是直接引用
- 举例子
- 节点1支配节点2
- 节点2支持节点3、4、6
- 节点3支配节点5
- 节点6支配节点7
- 节点5支配节点8
- 上面的例子有个不好理解的是节点2为什么支配了节点6?如果节点A存在于从根节点到节点B的每一个路径中,那么A就是B的支配项。2存在于1-2-4-6,也存在于1-2-3-6,所以节点2支配节点6

V8 新生代与老生代
- v8内存分为新生代和老生代内存,两块内存使用不同的内存GC策略
- 相比而言,新生代GC很快,老生代则较慢
- 新生代的内存在某些条件下会被转到老生代内存区
- GC发生时,用可能应用会暂停
解除引用的一些错误
var a = {name: 'wdd'}delete a.name // 这回让对象a变成慢对象var a = {name: 'wdd'}a.name = null // 这个则更好
关于slow Object
- V8 optimizing compiler makes assumptions on your code to make optimizations.
- It transparently creates hidden classes that represent your objects.
- Using this hidden classes, V8 works much faster. If you “delete” properties, these assumptions are no longer valid, and the code is de-optimized, slowing your code.
// Fast Objectfunction FastPurchase(units, price) {this.units = units;this.price = price;this.total = 0;this.x = 1;}var fast = new FastPurchase(3, 25);// Slow Objectfunction SlowPurchase(units, price) {this.units = units;this.price = price;this.total = 0;this.x = 1;}var slow = new SlowPurchase(3, 25);//x property is useless//so I delete itdelete slow.x;

Timers内存泄露
//var buggyObject = {callAgain: function () {var ref = this;var val = setTimeout(function () {console.log('Called again: '+ new Date().toTimeString());ref.callAgain();}, 1000);}};buggyObject.callAgain();buggyObject = null;
闭包内存泄露
var a = function () {var largeStr =new Array(1000000).join('x');return function () {return largeStr;};}();var a = function () {var smallStr = 'x',largeStr =new Array(1000000).join('x');return function (n) {return smallStr;};}();var a = function () {var smallStr = 'x',largeStr =new Array(1000000).join('x');return function (n) {eval(''); //maintains reference to largeStrreturn smallStr;};}();
DOM 内存泄露
leaf maintains a reference to it’s parent (parentNode), and recursively up to #tree, so only when leafRef is nullified is the WHOLE tree under #tree candidate to be GC

var select = document.querySelector;var treeRef = select("#tree");var leafRef = select("#leaf");var body = select("body");body.removeChild(treeRef);//#tree can't be GC yet due to treeReftreeRef = null;//#tree can't be GC yet, due to//indirect reference from leafRefleafRef = null;//NOW can be #tree GC
守则
- Use appropiate scope
- Better than de-referencing, use local scopes.
- Unbind event listeners
- Unbind events that are no longer needed, specially if the related DOM objects are going to be removed.
- Manage local cache
- Be careful with storing large chunks of data that you are not going to use.
分析内存泄漏的工具
- 浏览器:
performance.memory - devtool memory profile
关于闭包的提示
- 给闭包命名,这样在内存分析时,就可以按照函数名找到对应的函数
