Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

内存

1. V8 分代内存回收

两种内存

  1. 栈内存

    • 主要存放两种内容
      • 基本类型
      • 引用类型的内存地址
  2. 堆内存

    • 主要存放 js 对象
    • Node.js 对内存的 1.4GB 上限,就是指堆内存

回收方法

  1. 引用计数法

    • 标记每个变量被引用的次数,一旦减少到 0,代表成为孤儿,可以回收
    • 缺点:无法解决循环引用问题,永远不会减少到 0
  2. 标记清除法

    • 内存中变量之间会有关联,对象之间互相引用,会形成一棵以 window 对象作为根的树
      • 事件监听,DOM 对象,BOM 对象也可以作为根
    • 在堆中但不在树中的,称为不可达对象,就可以回收
    • 缺点:回收后,堆中会生成大量可用的碎片内存

V8 的分代回收

  1. 把内存分为两部分

    • 新生代
      • 新对象刚开始都会在这里面
      • 使用 Scavenge 复制算法
        • 把新生代内存分为两半
          • 一边是 From 空间,用于存放对象
          • 另一边是 To 空间,处于空闲状态
        • 当 From 空间不足时,会启动垃圾回收算法,把还存活的复制到 To 空间,复制完成后,两边角色互换
      • 需要结合晋升机制,把长时间存活的对象放到一个更大更高效的空间
        • 晋升条件(任意一个)
          • 对象已经历过一次垃圾回收
          • 在 To 空间的占用超过 25%
    • 老生代
      • 接收可能长时间存活的对象
      • 使用
        • 标记清除法
          • 日常标记出存活的对象,然后把其他清除
        • 标记压缩法
          • 当出现大量内存碎片,导致无法完整分配一块空间时,会启动这个算法,把存活对象向内存一端移动,执行速度较慢
  2. 导致的问题

    • 全停顿
      • 垃圾回收时,会阻塞 JavaScript 的执行,这叫全停顿
      • 为了减少全停顿带来的体验问题,V8 引入了增量标记、延迟清理等方法,把回收过程变成可拆分和停顿的

2. 内存泄漏

  1. 定义

    • 应该被回收的空间无法被回收,导致内存不断被占用,可用内存越来越少
  2. 为什么会影响性能?

    • 内存总是不够,导致垃圾回收频繁触发,产生全停顿
    • 内存中存在大量对象,导致垃圾回收算法执行变慢
  3. 原因

    • 一句话:被根节点误持有
    • 分情况
      • 把临时变量不断挂载到 window 对象和 DOM 对象上面
      • 意外的全局变量(比如let a = b = 1中的 b)
      • 大量事件监听,没有及时卸载
      • 被遗忘的定时器,没有及时清除
      • 闭包使用不当
  4. 检查办法

    • 【粗略】利用浏览器 F12 的 Performance 面板
      • 在 Memory 视图 中可以看到 Js 堆、Document、DOM、事件监听等内存的变化趋势,判断内存泄漏的地方
    • 【精准】利用浏览器 F12 的 Memory 面板
      • 专门针对堆内存,进行不同时间点的快照拍摄(Take snapshot),通过对比两次堆快照发现泄漏点