javascript 内存泄露
2019-12-09 本文已影响0人
_一九九一_
js内存溢出
JS程序的内存溢出后,表现为程序突然卡死
或假死
或报错
内存生命周期
- 分配你所需要的内存(变量、函数、对象执行时内存)
- 使用分配到的内存(读、写)
- 不需要时将其释放\归还(只回收局部变量,全局变量的生命周期直至浏览器卸载页面才会结束)
如时执行回收
- 局部变量只在函数的执行过程中存在,会为局部变量在栈或堆上分配相应的空间,以存储它们的值,然后在函数中使用这些变量,直至函数结束然后回收。
- 而闭包中由于内部函数的原因,外部函数并不能算是结束。
几种常见的js内存泄露
- 全局变量泄漏,调用的时候 this 指向了全局对象(window)
- 没有及时清理的计时器或回调函数
- 闭包, 事件处理回调,导致DOM对象和脚本中对象双向引用,这个时常见的泄漏原因
- script 中存在对DOM/BOM 对象的互相引用导致
例子
- f1函数
被调用时,进入fn1环境,开辟一块内存存放对象{name: 'aaa', age: 10},而当调用结束后,出了fn1的环境,该块内存会被js引擎中的垃圾回收器自动释放
; - f2函数
在fn2被调用,返回的对象被全局变量b所指向,该块内存并不会被释放
。
function fn1() {
var obj = {name: 'aaa', age: 10};
}
function fn2() {
var obj = {name:'bbb', age: 10};
return obj;
}
var a = fn1();
var b = fn2();
垃圾收集器是怎么判断哪个变量要回收( 标记清除和引用计数 )
标记清除和引用计数
- 引用计数不太常用,标记清除较为常用。
- 遍历所有可访问的对象。
- 回收已不可访问的对象。
标记清除
-
算法假定设置一个叫做根(root)的对象,垃圾回收器将定期从根开始扫描内存中的对象。
能从根部到达的对象,都是还需要使用的不回收
。那些无法由根部出发触及到的对象
被标记(标记阶段)
为不再使用,进行回收。 -
此算法可以分为两个阶段,一个是标记阶段(mark),一个是清除阶段(sweep)。
-
标记阶段
,垃圾回收器会从根对象开始遍历。每一个可以从根对象访问到的对象都会被添加一个标识,于是这个对象就被标识为可到达对象。 -
清除阶段
,垃圾回收器会对堆内存从头到尾进行线性遍历,如果发现有对象没有被标识为可到达对象,那么就将此对象占用的内存回收,并且将原来标记为可到达对象的标识清除,以便进行下一次垃圾回收操作。 - 在标记阶段,从根对象1可以访问到B,从B又可以访问到E,那么B和E都是可到达对象,同样的道理,F、G、J和K都是可到达对象。
所有未标记为可到达的对象都会被垃圾回收器回收
-
function test(){
var a = 1 ; // 被标记 ,进入环境
var b = 2 ; // 被标记 ,进入环境
}
test(); //执行完毕 之后 a、b又被标离开环境,被回收
到目前为止,IE9+、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。
引用计数
- 该算法假定设置一个叫做根(root)的对象,垃圾回收器将定期从根开始扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
- 有一个严重的问题:循环引用。
- 循环引用指的是对象A中包含一个指向对象B的指针,而对象B中也包含一个指向对象A的引用。
- 如果fn函数被大量调用,就会造成内存泄露。在IE7与IE8上,内存直线上升。
function test(){
var a = {} ; //a的引用次数为0
var b = a ; //a的引用次数加1,为1
var c =a; //a的引用次数再加1,为2
var b ={}; //a的引用次数减1,为1
}
function fn() {
var a = {};
var b = {};
a.pro = b;
b.pro = a;
}
fn();
GC根
- 一般指全局且不会被垃圾回收的对象,比如:window、document或者是页面上存在的dom元素。
- JavaScript的垃圾回收算法会判断某块对象内存是否是GC根可达(存在一条由GC根对象到该对象的引用),如果不是那这块内存将会被标记回收。
GC的缺陷
-
GC时,会停止响应其他操作
,这是为了安全考虑。 - 这就是新引擎需要优化的点:
避免GC造成的长时间停止响应
GC优化策略
Chrome V8 垃圾回收算法采用分代回收策略
- 通过区分「临时」与「持久」对象
- 多回收“临时对象”区,少回收“持久对象”区,减少每次需遍历的对象,减少每次GC的耗时
V8的内存限制
64位系统下,新生代内存大小为32MB,老生代内存大小为1.4GB。
32位系统新生代内存大小为16MB,老生代内存大小为700MB。
新生代内存:
- 存储的为存活时间较短的对象
老生代内存
- 存储的为存活时间较长或常驻内存的对象