JS垃圾回收

垃圾回收(GC)

js中的垃圾回收也叫GC.就是通过GC算法找到内存中的垃圾,然后对这个垃圾空间进行释放与回收.方便后续代码继续使用.
js中的内存管理是自动的,每当我们去创建一个对象的时候会自动去分配内存空间,后面通过判断确认其是否是垃圾.
当垃圾回收工作的时候,它会阻塞js代码的执行

一.js中的垃圾有哪些?

不被引用或从根上不可达的就是垃圾

  1. 对象不再被引用是垃圾
  2. 对象不能从根上访问时是垃圾(局部作用域,闭包除外)
    • js中的根可以理解为全局对象,也叫全局上下文
let obj = {name:'xm'} // 这里创建并使用了一个小明的内存空间,然后被obj引用了.而且这个空间能通过obj从全局被拿到
obj = null  // 这里将obj与小明的引用断掉了,小明的空间没有其它引用所以无法从根访问到了,固然会被垃圾回收
  1. 程序中不再需要使用的对象
function func(){
    name = 'lg'
    return `${name} is a coder`
}
func() // 函数调用完成之后 name不再被使用
  1. 程序中不能再访问的对象
function func(){
    const name = 'lg'
    return `${name} is a coder`
}
func() // 函数调用完成之后 name不再被使用 且外部无法访问

二.GC算法

GC是一种机制,查找垃圾,释放,回收,分配空间时所遵循的规则叫GC算法

  • 引用计数
    设置引用数,判断当前引用数是否为0(如果为0就是垃圾).当引用关系发生改变时修改引用数字
    优点: 发现垃圾立即回收,减少程序卡顿时间
    缺点:

    • 无法回收循环引用的对象,如下:首先fn中的obj1与obj2是无法在全局上下文中访问的,但是他们的引用数不为0,所以回收不了
    function fn() {
        const obj1 = {}
        const obj2 = {}
        obj1.name = obj2
        obj2.name = obj1
        return 'xx'
    }
    
    • 时间开销大(需要时刻监听引用数的变化,而且需要数值也需要耗费时间)
  • 标记清除
    分两个阶段进行.遍历所有可达对象标记活动对象,不可达对象无法被标记. 遍历所有对象清除没有标记的对象,回收后的空间会放到空闲链表中.完成一次垃圾回收之后清除所有标记
    优点: 可以回收循环引用的对象
    缺点:

    • 容易产生空间碎片化.由于垃圾对象在地址上是不连续的,所以回收后的空间也不是连续的,造成后续在使用的时候就要匹配内存大小.
    • 不能立即回收垃圾对象,因为它要在最后才能清除
      在这里插入图片描述
  • 标记整理
    可以看做是标记清除的增强版,只不过在清除阶段会先执行整理内存空间,移动对象的位置让地址产生连续.
    优点: 解决了空间碎片化的问题
    缺点: 不能立即回收垃圾对象
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • V8垃圾回收(分代式回收机制)

三.认识V8

  • V8是一款主流的JS执行引擎,在谷歌和node中都用的v8
  • V8采用即时编译,所以很快
  • V8内存设限 (本身是为浏览器而设计的,不需要太大的内存.在64位操作系统,内存在1.5G(这个内存下消耗的时间也才1秒) 32位操作系统,内存在800M.)

V8垃圾回收策略

在这里插入图片描述

  • 基于V8内存设限,采用分代回收的思想(V8内存空间一分为二,小空间存储新生代,大空间存储老生代).针对不同对象采用不同的算法
    • 按内存分为新生代和老生代
    • 新生代(64位32M,32位16M,因为分配的内存比较小,所以采用一分为二的方法,用空间换时间)
      • 指的是存活时间较短的对象(比如局部作用域中的变量)
      • 回收过程采用复制算法和标记整理
      • 内存区分为两个相同大小的空间(From,To)
      • 使用空间为From,所有活动对象存储在From空间.To是空闲空间
      • 当From空间应用到一定程度之后就会触发GC操作,将活动对象进行整理拷贝至空闲空间To(也叫翻转).这样新生代就完成了空间的释放与回收操作
    • 老生代(64位1.4G,32位700M,内存相对比较大不需要像新生代一样一分为二)
      • 指的是存活时间较长的对象(比如全局作用域或闭包中的变量)
      • 当一个对象经过多次复制依然存活时,它将会被认为是生命周期较长的对象。
        这种较长生命周期的对象随后会被移动到老生代中,采用新的算法进行管理。
        对象从新生代中移动到老生代中的过程称为晋升。
      • 晋升条件
        对象晋升的条件主要有两个。
        1、对象在新生代期间是否经历过算法回收;
        2、To空间的内存占用比超过限制(To空间内存消耗是否超过25%,如果超过对象直接晋升)
      • 回收过程主要采用标记清除,当新生代移过来而老生代空间不够用时会采用标记整理,最后采用增量标记进行效率优化

标记增量如何优化垃圾回收?

让gc操作与代码交替运行,解决长时间阻塞问题
在这里插入图片描述

界定内存问题的标准
  • 内存泄露:内存使用持续升高
  • 内存膨胀(浏览器为了满足代码需求,主动申请的内存. 也有可能是设备配置的问题,带动不了. 这里要定位是设备问题还是代码问题)
  • 频繁的垃圾回收(让用户感觉卡顿):Timeline中频繁的上升下降,任务管理器中数据频繁的增加减小
内存监控
  • 浏览器任务管理器
  • Timeline时序图记录
  • 对快照查找分离DOM(分离DOM是一种内存泄露)

V8引擎工作流程(解析和编译js代码)

在这里插入图片描述

V8只是浏览器渲染引擎里执行js代码的一个组成部分

  • Scanner词法分析器
    是一个Js代码的扫描器,用来对js代码进行词法分析,它会把代码分析成不同的tokens.如下
    在这里插入图片描述在这里插入图片描述

  • Parser全解析

    • 解析被使用的代码
    • 生成AST
    • 构建具体scopes信息,变量引用,声明等
    • 抛出所有语法错误
      会将词法分析之后的tokens解析成AST语法树,同时在分析中对语法进行校验,如果有语法错误就会抛出错误.
      在这里插入图片描述
  • Preparser预解析

    • 跳过未被使用的代码
    • 不生成AST,但是会创建作用域信息
    • 依据规范抛出特定错误
    • 解析速度更快
      在这里插入图片描述
  • Ignition解释器
    将生成的AST语法树转为字节码

  • TurboFan编译器
    字节码转为汇编代码(机器码),之后的话就可以开始代码的执行了

如何优化JS代码

在构造函数中声明对象属性
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

var p1 = new Point(11, 22);  // hidden class Point created
var p2 = new Point(33, 44);

p1.z = 55;  // another hidden class Point created

更改对象属性会产生新的隐藏类。正如你所见,p1和p2现在有不同的隐藏类了。这阻碍了TurboFan的优化尝试:具体来说,任何接受Point对象的方法现在都是去优化的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值