[javascript核心-08] V8 内存管理机制及性能优化

news2024/11/25 0:45:46

V8 内存管理

V8 本身也是程序,它本身也会申请内存,它申请的内存称为常驻内存,而它又将内存分为堆和栈

栈内存

栈内存介绍

  1. 栈用于存放JS 中的基本类型和引用类型指针
  2. 栈空间是连续的,增加删除只需要移动指针,操作速度很快
  3. 栈空间是有限的,若超出栈空间内存,会抛出栈空间溢出错误
  4. 栈是在执行函数时创建的,函数执行完毕后,栈销毁

栈的内存回收机制

  1. 栈中压入一个全局执行上下文,长驻栈底,不会销毁
  2. 每个函数执行时都会创建一个执行上下文(存储函数变量、文法环境、词法环境的对象)
  3. 函数执行结束,立马销毁
  4. 当函数中存在闭包的情况下,函数的执行上下文会被销毁。函数内部被外部引用的变量会被放入堆内存中保存。

堆内存

内存不连续,需要大的内存空间,使用堆

在 32 位系统下是1.4G,64 位下分配 2G

堆内存分类

新生代

新生代内存new space会保存一些生命周期较短的对象,但新创建的对象无法确定其生命周期,因此会先放入新生代内存中。不过它只有 32M 大小

老生代

新生代内存old space会保存一些生命周期较长的对象。如果判断长短?两个周期的垃圾回收之后,如果数据会在新生代内存中,则将其放入老生代内存。

比较大的对象直接放入老生代。

代码空间(code space)

运行时代码空间,存放 JIT已编译代码,是唯一拥有执行权限的内存

大对象空间(large object space)

专门存储大对象,新生代存放不下,单独分配内存存储。GC 不会对其进行垃圾回收

Map Space

存放对象的Map 信息,即隐藏类,它是为了提升对象属性的访问速度的。V8 会为每一个对象创建一个隐藏类,记录对象的属性布局,包括所有属性和偏移量。

垃圾回收机制

何为垃圾?程序执行结束后不再需要的数据。更准确一点,从根节点GCRoot访问不到的就是内存垃圾。

新生代垃圾回收

新生代中有From SpaceTo Space两块区域,新对象会放入From Space中,如果内存满了就开始垃圾回收。

From SpaceTo Space双端队列的数据结构。

执行过程

  1. 从根节点出发,广度优先遍历所有能到达的对象,将存活的对象(可以访问到的对象)按顺序复制到To Space内存中
  2. 遍历完成后,清空To Space内存
  3. From SpaceTo Space角色互换

晋升机制

  1. 经过一次GC 还存活
  2. 达到空间限制

晋升机制.png

优点:空间连续,不会造成内存碎片;高效

缺陷:浪费空间

老生代垃圾回收

浏览器不会同时采用标记清除和标记整理两种方式,在多次标记清除之后通过标记整理去处理内存碎片的问题。

标记清除

标记:从根节点出发,深度优先遍历,能够达到的表示活对象,标记为黑色,不能达到的对象表示死对象,标记为白色。

清除:清除死亡对象,释放内存

优点:不需要进行元素移动;不需要空闲区域,节省内存

缺陷:一次标记清楚后,内存空间不连续,会出现许多内存碎片

标记整理

从根节点出发,深度优先遍历,能够达到的表示活对象,标记为黑色,不能达到的对象表示死对象,标记为白色。这个过程中如果发现内存不连续,则进行元素移动,放到前一个元素后面,保证内存的连续。最后将后面的全部清除。

优点:能够解决标记清除带来的内存碎片问题,特点是一边标记一边进行内存整理。

缺陷:需要移动元素

垃圾回收的性能回收

垃圾回收机制属于宏任务,需要 JS 主线程调用。如果执行时间过长,会造成卡顿,阻塞 JS 的执行。

为了不阻塞 JS执行,需要进行优化。优化的思想可以是将大任务拆成任务,分步执行,类似 React的fiber;也可以将一些任务放入后台,由其他线程执行,不占用主线程

并行执行

开启辅助线程来执行新生代的垃圾回收工作,并行执行回收也是会让 JS 进入全停顿的状态,主线程不能进行任何操作,只能辅助线程完成。但由于新生代比较小,因此不会造成长时间的卡顿。

增量标记

老生代内存空间又大,存储的数据又多,因此不适合并行执行的优化,仍然会占用很长的时间。因此采用分批标记,不一次性完成,在 JS 空闲的时候执行。将标记工作分为多个阶段,每个阶段都只标记一部分对象,和主线程穿插进行。

但是这样就会有一个中间状态出现了,因为除了黑色表示活对象,白色表示死对象以外,还需要有一个种来表示还未被标记的状态。

为了支持增量标记,V8 需要支持垃圾回收的暂停和恢复,因此采用黑白灰三色标记。

  1. 黑色表示该节点可被 GC 根节点到达,且其全部子节点已经被标记完毕。

  2. 灰色表示该节点可被 GC 根节点到达,但子节点还没有被标记处理,也表示目前正在处理该节点

  3. 白色表示该节点还未被被 GC 到达,如果 GC 结束还没有处理,就表明无法到达,没有被引用,就会被回收。

惰性清理

增量标记完成后,如果内存足够则不清理,等 JS 执行完毕再清理。

并发回收

主线程执行过程中,辅助线程在后台完成垃圾回收工作。标记操作由辅助线程完成,清理操作由主线程和辅助线程配合完成。

并发和并行:并发是上下文快速切换;并行是同一时刻多个进程同时进行。

内存泄漏

会产生内部泄漏的情况:

隐式全局变量

全局变量不会被垃圾回收,依次要将不再使用的全局变量及时手动设置为null

function fn(){
    a = 10
}

上面的代码中,a就是全局变量永不回收

剥离的 DOM

在界面中移除 DOM 元素时,还应该相应的移除该其他的引用

<div id='container'>
    <div id='box'></div>
</div>

<script>
    let container = document.getElementById('container')
    let box = document.getElementById('box')
    document.body.removeChild(container)
</script>

上面的代码中,DOM 中已经移除了container节点,但是它还被变量container引用,因此不会被垃圾回收。

定时器

若不手动清除定时器,则其到时执行后也不会被垃圾回收,应该用对应的函数清除定时器

function fn(){
    let i = setInterval(()=>{

    },1000)
    clearInterval(i)
}
fn()

事件监听

如果监听函数中使用到了外部对象数据,则需要进行手动清除事件监听,不然不会被垃圾回收

function fn(){
    let data = [1,2,3]
    class App {
        handle = ()=>{
            console.log(data)
        }
        mount(){
            document.addEventListener('click', this.handle)
        }
        unmount(){
            document.removeEventListener('click', this.handle)
        }
    }
    let app = new App()
    return app
}
let app = fn()
app.mount()
app.unmount()

Map/Set

不主动清除不会被主动回收

let arr = [1,2,3]
let set = new Set()
set.add(arr)
arr = null

上面的代码中,即使arr手动指向了null,但是数据仍然不会被垃圾回收,因为只是清除了arr对数据对象的引用,而set还引用了该数据,因此不会被垃圾回收,必须再手动将set设置为null,才能释放arr。但是set中其他数据的引用可能是必须保留的,因此这种情况下最好的方式是使用WeakSet,以及WeakMap

let arr = [1,2,3]
let set = new WeakSet()
set.add(arr)
arr = null

上面的代码arr指向的数组就会被垃圾回收,因为WeakSet的引用是弱引用,不会阻止垃圾回收

console

浏览器会保存我们输出对象的信息数据引用,因此未清理的console也会造成内存泄漏

排查内存泄漏

如何知道是哪里的代码造成了内存泄漏呢?

录制⏺监控

  1. 刷新页面加载
  2. 监控 JS 堆、文档、节点、监听器、GPU
  3. 手动进行 GC,触发垃圾回收

内存占用

查看内存占用及性能分析:Performance

<body>
    <script>
        let arrData = new Array(200000).fill(100)
        console.log(arr)
    </script>
</body>

image.png

能够看到 JS 堆内存是陡峭上升

快照对比:Memory

<body>
    <button id="btn">增加内存使用</button>
    <script>
        let btn = document.getElementById('btn')
        btn.onclick = fn()
        function Person(name){this.name = name}
        function fn(){
            var arr = []
            for (let index = 0; index < 10000; index++) {
                arr.push(new Person(index))
            }
            console.log(arr)
        }
    </script>
</body>

对于上面的代码,可以在执行前面进行两次快照录制,对比内存占用情况

image.png

可以看到Person对象时增加了 10000 次,然后我们去看这些对象是在哪里产生的

51686123059_.pic.jpg

然后我们就可以找到内存泄漏的问题,进行优化处理

61686123083_.pic.jpg

从垃圾回收角度看性能优化

少用全局变量

  1. 全局执行上下文会一直存在于上下文执行栈中,不会被销毁
  2. 查找链条比较长,比较销毁性能
  3. 可以使用局部缓存替代全局变量

通过原型新增方法

减少函数对象方法的内存占用,让对象实例都共用同一个函数对象。

对象结构保持稳定和一致

  1. V8 会为每个对象增加一个隐藏类,如果对象发生改变(属性改变)就会重置隐藏类,而结构相同的对象会共享隐藏类。因此对象属性尽量保持一致,且尽量不要动态添加和删除属性
  2. 隐藏类描述了对象的结构和属性的偏移地址,可以增加查找属性的时间

结构一致,共享隐藏类

let p1 = {name:'flten',age:18}
let p2 = {name:'wall',age:20}

对象结构不稳定,隐藏类频繁重建

let p = {}
p.name = 'flten'
p.name = 22

函数参数尽量保持稳定

V8 中的内联缓存会监听函数执行,记录中间数据,如果参数结构不同优化会失效。因此尽量不要使用动态参数,让参数结构保持稳定。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/771928.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动收小麦机(牛客2023萌新)

题目链接 示例1 输入 复制 4 1 2 1 1 4 5 2 2 2 3 4 输出 复制 10 说明 在第4格放出水流后&#xff0c;水流会流向第3格&#xff0c;由于第3格高度比第4格低&#xff0c;所以水流继续向左流向第2格&#xff0c;因为平地水流只能流2格&#xff0c;所以到达第2格后水流停…

栈练习题(逆波兰表达式,有效括号,出入栈次序匹配,最小栈)

目录 基础知识: 中缀表达式和后缀表达式(逆波兰式) 中缀表达式转后缀表达式 后缀表达式求结果 有效括号 栈的压入,弹出序列 最小元素栈 基础知识: 栈:是一种先入后出的数据结构,它的底层是由数组实现的 入栈:push(),出栈pop(),查看栈顶元素peek() 中缀表达式和后缀表…

docker-compose搭建prometheus+grafana+钉钉告警

前言&#xff1a; 本文将介绍使用docker-compose部署搭建promtheus监控容器、主机、服务等相关状态&#xff1b; 配合granfana面板构建监控大屏&#xff1b; 由于grafana的报警不是很友好&#xff0c;使用dingtalk&#xff0c;配合altermanager&#xff0c;实现钉钉报警。 …

Crosswalk App UI 自动化

目录 前言&#xff1a; Crosswalk WebDriver 实现 Crosswalk app UI 自动化实现 前言&#xff1a; Crosswalk是一个基于Chromium的开源项目&#xff0c;它提供了一个跨平台的应用程序运行时环境&#xff0c;可以帮助开发人员在移动设备上构建高性能的Web应用程序。在Crossw…

“体验家”亮相第六届IAIC成都国际医美产业大会

6月23日-25日&#xff0c;第六届IAIC成都国际医美产业大会暨“医美之都”高峰会议在成都世纪城国际会议中心成功举行。本次大会邀请了来自国家药品监督管理局、部分省市地区的相关领导莅临指导&#xff0c;以及来自全国100医美行业头部平台&#xff0c;近2000位医美产业领军代表…

关于前后端JSON解析差异问题与思考

目录 一、问题回顾 二、问题思考 2.1 JavaScript如何解析json字符串 2.2 Java如何解析json字符串 2.3 Java和JavaScript交互如何出现JSON解析问题 三、经验总结 本文主要总结了作者在一次涉及流程表单的需求发布中遇到的问题及思考总结。 一、问题回顾 在一次涉及流程表…

FPGA驱动SPI屏幕(附完整工程)

一. 简介 相信大家都玩过屏幕&#xff0c;在FPGA上使用最多的就是VGA/HDMI接口的显示器了&#xff0c;这两种显示器的优点就不用说了&#xff0c;缺点就是体积比较大&#xff0c;而且价格比较贵&#xff0c;对于追求便携/价格低的我来说&#xff0c;SPI接口的屏幕才是我的首要…

实战操作接口自动化测试

最近接到一个接口自动化测试的case&#xff0c;并展开了一些调研工作&#xff0c;最后发现&#xff0c;使用pytest测试框架并以数据驱动的方式执行测试用例&#xff0c;可以很好的实现自动化测试。这种方式最大的优点在于后续进行用例维护的时候对已有的测试脚本影响很小。当然…

文件IO_文件截断_ftruncate,truncate(附Linux-5.15.10内核源码分析)

目录 1.为什么需要文件截断&#xff1f; 2.truncate函数介绍 2.1 truncate函数 2.2 truncate函数内核源码分析 2.3 truncate函数使用示例 3.ftruncate函数介绍 3.1 ftruncate函数 3.2 ftruncate函数内核源码分析 3.3 ftruncate函数使用示例 3.4 ftruncate和文件偏移量…

8年测试总结,App自动化测试-Appium常遇问题+解决(详细整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 问题1&#xff1a…

小白入门C#编写MVC登录小案例

一、C#编写MVC登录小案例 &#x1f680;1. 新建MVC项目。 &#x1f680;2. 在Models文件夹下创建一个User类&#xff0c;包含登录所需要的用户名和密码属性。 namespace MvcLogin.Models {public class User{public string UserName{get; set;}public string Password{get;se…

基于Java+SpringBoot+Vue+Uniapp前后端分离考试学习一体机设计与实现(视频讲解,已发布上线)

博主介绍&#xff1a;✌全网粉丝3W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

计算机基础专升本笔记三 计算机中的数据表示、编码

计算机基础专升本笔记三计算机中的数据表示、编码 一、计算机中的数据的单位 &#xff08;一&#xff09;数据存储的单位有哪些&#xff1f; 计算机存储单位有 bit, Byte, KB, MB, GB, TB, PB, EB, ZB, BB来表示。我们经常将Byte简称为B&#xff0c;将KB简称K。 &#xff08…

微信怎么自动加好友,通过好友后自动打招呼

很多客户朋友每天花大量的时间用手机搜索添加好友&#xff0c;这样的添加很集中也容易频繁&#xff0c;而且效率还低。对方通过后&#xff0c;有时也不能及时和客户搭建链接&#xff0c;导致客户也流失了。 现在可以实现自动添加和自动打招呼哦&#xff0c;只需要导入数据、设置…

linux查看ipynb文件

linux查看ipynb文件 使用jupyter查看 使用jupyter查看 安装 pip install jupyter添加配置好的环境到jupyter notebook的kernel中&#xff1a; python -m ipykernel install --user --name mmdet --display-name "mmdet"运行jupyter notebook &#xff08;在ipynb…

精选了6款好用的AI绘画工具,值得一试

近几年来&#xff0c;伴随着AI技术的发展&#xff0c;设计领域发生了巨大的变化。AI绘图工具的出现很大程度上减轻了设计师的工作负担&#xff0c;本文精选了6款优秀的AI绘图工具为大家推荐&#xff0c;一起来看看吧&#xff01; 1、即时灵感 即时灵感作为国产的AI绘图工具&a…

相机标定学习笔记

Kalibr 是标定工具中&#xff0c;唯一一个可以标定camToImu的&#xff0c;是vio必不可少的工具&#xff0c;其他的都有替代品。所以学习多种开源算法进行相机标定&#xff0c;并记录学习相机标定的过程。 一、相机标定 1、在场景中放置一个已知的物体 &#xff08;1&#xff…

ENSP实验四:搭建VPN(GRE,配置安全策略)

首先分析一下数据的流向&#xff1a; PC1->PC2 1、FW1&#xff1a;trust->dmz 【192.168.1.1->192.168.2.1 ICMP】 2、AR1->AR2&#xff1a;【202.1.1.1->202.1.3.1|GRE|192.168.1.1->192.168.2.1 icmp】 3、FW2&#xff1a; ①untrust->local …

提示工程师:如何写好Prompt

提示工程由来 提示工程是一门相对较新的学科&#xff0c;用于开发和优化提示以有效地将语言模型 (LM) 用于各种应用程序和研究主题。 研究人员使用提示工程来提高 LLM 在广泛的常见和复杂任务&#xff08;例如问题回答和算术推理&#xff09;上的能力。 开发人员使用提示工程…

【图像处理OpenCV(C++版)】——5.6 图像平滑之联合双边滤波

前言&#xff1a; &#x1f60a;&#x1f60a;&#x1f60a;欢迎来到本博客&#x1f60a;&#x1f60a;&#x1f60a; &#x1f31f;&#x1f31f;&#x1f31f; 本专栏主要结合OpenCV和C来实现一些基本的图像处理算法并详细解释各参数含义&#xff0c;适用于平时学习、工作快…