JavaScript进阶(十五):JS 垃圾回收机制_vue gc,如何自学前端开发

96 阅读8分钟
function objGroup(obj1, obj2) {
    obj1.next = obj2
    obj2.prev = obj1

    return {
        o1: obj1,
        o2: obj2,
    }
}

let obj = objGroup({name: 'obj1'}, {name: 'obj2'})
console.log(obj)

上面的这个例子中,obj1和obj2通过各自的属性相互引用,所有它们的引用计数都不为零,这样就不会被垃圾回收机制回收,造成内存浪费。

引用计数算法其实还有一个比较大的缺点,就是我们需要单独拿出一片空间去维护每个变量的引用计数,这对于比较大的程序,空间开销还是比较大的。

引用计数算法优点:

  • 引用计数为零时,发现垃圾立即回收;

  • 最大限度减少程序暂停;

引用计数算法缺点:

  • 无法回收循环引用的对象;

  • 空间开销比较大;

3.1.2 标记清除(Mark-Sweep)

核心思想:分标记清除两个阶段完成。

  1. 遍历所有对象找标记活动对象;

  2. 遍历所有对象清除没有标记对象;

  3. 回收相应的空间。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
标记清除算法的优点是:对比引用计数算法,标记清除算法最大的优点是能够回收循环引用的对象,它也是v8引擎使用最多的算法。

标记清除算法的缺点是:
在这里插入图片描述

上图我们可以看到,红色区域是一个根对象,就是一个全局变量,会被标记;而蓝色区域就是没有被标记的对象,会被回收机制回收。这时就会出现一个问题,表面上蓝色区域被回收了三个空间,但是这三个空间是不连续的,当我们有一个需要三个空间的对象,那么我们刚刚被回收的空间是不能被分配的,这就是“空间碎片化”。

3.1.3 标记整理(Mark-Compact)

为了解决内存碎片化的问题,提高对内存的利用,引入了标记整理算法。

标记整理可以看做是标记清除的增强。标记阶段的操作和标记清除一致。

清除阶段会先执行整理,移动对象位置,将存活的对象移动到一边,然后再清理端边界外的内存。

标记整理的缺点是:移动对象位置,不会立即回收对象,回收的效率比较慢。

3.1.4 增量标记(Incremental Marking)

为了减少全停顿的时间,V8对标记进行了优化,将一次停顿进行的标记过程,分成了很多小步。每执行完一小步就让应用逻辑执行一会儿,这样交替多次后完成标记。
在这里插入图片描述

长时间的GC,会导致应用暂停和无响应,将会导致糟糕的用户体验。从2011年起,v8就将「全暂停」标记换成了增量标记。改进后的标记方式,最大停顿时间减少到原来的1/6。

四、v8 引擎垃圾回收策略

  • 采用分代回收的思想;

  • 内存分为新生代、老生代;
    在这里插入图片描述

针对不同对象采用不同算法:

  • 新生代:对象的存活时间较短。新生对象或只经过一次垃圾回收的对象。

  • 老生代:对象存活时间较长。经历过一次或多次垃圾回收的对象。

在这里插入图片描述
V8堆的空间等于新生代空间加上老生代空间。且针对不同的操作系统对空间做了内存限制。

类型 \ 系统位数64位32位
老生代1400MB700MB
新生代32MB700MB

限制内存的原因:

  • 针对浏览器来说,这样的内存是足够使用的。

  • 针对浏览器的GC机制,经过不断的测试,如果内存再设置大一点,GC回收的时间就会达到用户的感知,会造成感知上的卡顿。

4.1 回收新生代对象

回收新生代对象主要采用复制算法Scavenge 算法)加标记整理算法。而Scavenge 算法的具体实现,主要采用了Cheney算法

Cheney算法将内存分为两个等大空间,使用空间为From,空闲空间为To

检查From空间内的存活对象,若对象存活,检查对象是否符合晋升条件,若符合条件则晋升到老生代,否则将对象从 From 空间复制到 To 空间。若对象不存活,则释放不存活对象的空间。完成复制后,将 From 空间与 To 空间进行角色翻转。

4.1.1 对象晋升机制

一轮GC还存活的新生代需要晋升。
当对象从From 空间复制到 To 空间时,若 To 空间使用超过 25%,则对象直接晋升到老生代中。设置为25%的比例的原因是,当完成 Scavenge 回收后,To 空间将翻转成From 空间,继续进行对象内存的分配。若占比过大,将影响后续内存分配。

4.2 回收老生代对象

回收老生代对象主要采用标记清除标记整理增量标记算法,主要使用标记清除算法,只有在内存分配不足时,采用标记整理算法。

  1. 首先使用标记清除完成垃圾空间的回收

  2. 采用标记整理进行空间优化

  3. 采用增量标记进行效率优化

4.2.1 新生代和老生代回收对比
  • 新生代由于占用空间比较少,采用空间换时间机制。

  • 老生代区域空间比较大,不太适合大量的复制算法和标记整理,所以最常用的是标记清除算法,为了就是让全停顿的时间尽量减少。

4.3 内存泄漏识别方法

我们先写一段比较消耗内存的代码:

<button class="btn">点击</button>

<script>
    const btn = document.querySelector('.btn')
    const arrList = []

    btn.onclick = function() {
        for(let i = 0; i < 100000; i++) {
            const p = document.createElement('p')
            // p.innerHTML = '我是一个p元素'
            document.body.appendChild(p)
        }

        arrList.push(new Array(1000000).join('x'))
    }
</script>

使用浏览器的Performance来监控内存变化
在这里插入图片描述
点击录制,然后我们操作消耗性能的操作,操作完成之后,点击stop停止录制。
在这里插入图片描述
然后我们看一看是哪些地方引起了内存的泄漏,我们只需要关注内存即可。
在这里插入图片描述

可以看到内存在短时间内消耗的比较快,下降的小凹槽,就是浏览器在进行垃圾回收。

五、性能优化

5.1 避免使用全局变量

  • 全局变量会挂载在window下;

  • 全局变量至少有一个引用计数;

  • 全局变量存活更久,持续占用内存;

  • 在明确数据作用域的情况下,尽量使用局部变量;

5.2 减少判断层级

function doSomething(part, chapter) {
    const parts = ['ES2016', '工程化', 'Vue', 'React', 'Node']

    if (part) {
        if (parts.includes(part)) {
            console.log('属于当前课程')
            if (chapter > 5) {
                console.log('您需要提供 VIP 身份')
            }
        }
    } else {
        console.log('请确认模块信息')
    }
}

doSomething('Vue', 6)

// 减少判断层级
function doSomething(part, chapter) {
    const parts = ['ES2016', '工程化', 'Vue', 'React', 'Node']

    if (!part) {
        console.log('请确认模块信息')
        return
    }

    if (!parts.includes(part)) return
    console.log('属于当前课程')

    if (chapter > 5) {
        console.log('您需要提供 VIP 身份')
    }
}

doSomething('Vue', 6)

5.3 减少数据读取次数

对于频繁使用的数据,我们要对数据进行缓存。

<div id="skip" class="skip"></div>

<script>
 var oBox = document.getElementById('skip')

 // function hasEle (ele, cls) {
 // return ele.className === cls
 // }

 function hasEle (ele, cls) {
 const className = ele.className
 return className === cls
 }

 console.log(hasEle(oBox, 'skip'))
</script>

5.4 减少循环体中的活动

var test = () => {
    var i
    var arr = ['Hello World!', 25, '岂曰无衣,与子同袍']
    for(i = 0; i < arr.length; i++) {
        console.log(arr[i])
    }
}

// 优化后,将arr.length单独提出,防止每次循环都获取一次
var test = () => {
    var i
    var arr = ['Hello World!', 25, '岂曰无衣,与子同袍']
    var len = arr.length
    for(i = 0; i < len; i++) {
        console.log(arr[i])
    }
}

5.5 事件绑定优化

<ul class="ul">
    <li>Hello World!</li>
    <li>25</li>
    <li>岂曰无衣,与子同袍</li>
</ul>

<script>
    var list = document.querySelectorAll('li')
    function showTxt(ev) {
        console.log(ev.target.innerHTML)
    }

    for (item of list) {
        item.onclick = showTxt
    }

    // 优化后
    function showTxt(ev) {
        var target = ev.target
        if (target.nodeName.toLowerCase() === 'li') {
            console.log(ev.target.innerHTML)
        }
    }

    var ul = document.querySelector('.ul')
    ul.addEventListener('click', showTxt)
</script>

5.6 避开闭包陷阱

<button class="btn">点击</button>

<script>
    function foo() {
        let el = document.querySelector('.btn')
        el.onclick = function() {
            console.log(el.className)
        }
    }
    foo()

    // 优化后
    function foo1() {


### 最后

推荐一些系统学习的途径和方法。  

![路线图](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/b3140350472142da91b9f3859b0f6360~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3NTc5MjMwMTY3MDI=:q75.awebp?rk3s=f64ab15b&x-expires=1771411213&x-signature=wgLczDZ%2Fr40BxDSq2%2Bi4sd0TY5o%3D)

每个Web开发人员必备,很权威很齐全的Web开发文档。作为学习辞典使用,可以查询到每个概念、方法、属性的详细解释,注意使用英文关键字搜索。里面的一些 HTML,CSS,HTTP 技术教程也相当不错。



HTML 和 CSS:

![html5知识](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/39dfe7cefceb45ed8a6a6e5316429659~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3NTc5MjMwMTY3MDI=:q75.awebp?rk3s=f64ab15b&x-expires=1771411213&x-signature=379QmP1fPd%2BGCMei8MB8SDOlldI%3D)

![css基础知识](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/cbdd311855c54295b93048d578fe850a~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3NTc5MjMwMTY3MDI=:q75.awebp?rk3s=f64ab15b&x-expires=1771411213&x-signature=YIk7CFEQ2k1rAcZ2b0jFzHDX13g%3D)


**开源分享:https://docs.qq.com/doc/DSmRnRGxvUkxTREhO**