Js中的内存管理

2020-03-21

内存数据结构


  • 栈:由操作系统自动分配释放 ,存放函数的参数值,局部变量等。
    js中用来存储基本数据类型,它们占据空间小、大小固定,被频繁使用。
    numberstringbooleanundefinednullsymbol
  • 堆:一般由程序员分配释放,生命周期由垃圾回收算法决定。
    js中用来存储引用数据类型,它们占据空间大、大小不固定。
    objectarrayfunction


内存溢出/泄露


内存溢出:已有的数据超过了其获得到的内存所能存储的范围
内存泄露:不再用到的内存没有及时释放,导致占用的内存不能使用或回收。

如下原因会导致内存泄露:

  • 全局变量不会被回收
  • 闭包可以维持函数内局部变量,使其得不到释放
  • 未清理的DOM元素的引用
    比如删除DOM时,事件未清除
  • 被遗忘的计时器
  • 控制台日志
  • 循环引用(两个对象相互引用)
    引用计数机制时会产生此问题,但在标记清除机制中已被解决


垃圾回收机制 GC (Garbage Collection)


js创建一个对象时,会自动为其分配适当的内存,垃圾回收器定期扫描对象,判断对象是否可以回收,释放他们所指向的内存,防止内存泄漏。有以下两种方式:

1. 引用计数 Reference Counting (低版本IE)

跟踪一个变量的引用次数,当引用次数为0时就将其回收。
这种方式不能解决循环引用的问题,容易引起内存泄漏。

1
2
3
4
5
6
// 每次调用f(), a b都互相引用,这部分内存永远不会被释放
function f() {
var a = {}, b = {};
a.prop = b;
b.prop = a;
}

2. 标记清除 Mark and Sweep (主流方式)

从全局作用域开始一层层往下标记能引用到的对象,未被标记的对象为不可访问的,被删除。解决了循环引用导致内存泄露的问题。



V8 引擎


chrome的js解释器,虽然十分强大,但存在内存溢出的隐患,64位机器只被分配1.4G的内存,在客户端浏览器绰绰有余,但在后端NodeJs上就不够了。

V8内存空间分为新生代和老生代:

1. 新生代内存空间:存放存活时间较短的对象

Scavenge 算法: from相当于清空区,to相当于安全区

2. 老生代内存空间:存放存活时间较长的对象

  • 标记清除GC
    标记存活对象,未标记的被清除。垃圾回收后内存会出现不连续的情况

  • 标记合并GC
    将清除后保留下来的对象往堆的另一端移动,解决内存碎片化问题

3. 新生代晋升到老生代

  1. 新生代垃圾回收中,若对象之前被清理过,那么将其晋升到老生代内存中
  2. from和to反转过程中,若to使用量超过25%,就将from中对象晋升到老生代