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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

leetcode100.相同的树

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;相同的树 1️⃣ 代码&#xff1a; bool isSameTree(struct TreeNode* p, struct TreeNode* q){// 判断两棵树当前结点是否为空if (p NULL && q NULL) {// 说明是相同的return true;}// 来到这里有几种情况// …

单片机第一季:零基础6——按键

目录 1&#xff0c;独立按键 2&#xff0c;矩阵按键 &#xff08;注意&#xff1a;文章中的代码仅供参考学习&#xff0c;实际使用时要根据需要修改&#xff09; 1&#xff0c;独立按键 按键管脚两端距离长的表示默认是导通状态&#xff0c;距离短的默认是断开状态&#xf…

集群基础3——haproxy负载均衡apache

文章目录 一、环境说明二、安装配置httpd三、安装配置haproxy四、验证http负载均衡五、配置https负载均衡六、haproxy网页监控6.1 监控参数详解6.2 页面操作 一、环境说明 使用haproxy对apache进行负载均衡。 主机IP角色安装服务192.168.161.131后端服务器1httpd,80端口192.168…

前端 | (五)CSS三大特性及常用属性 | 尚硅谷前端html+css零基础教程2023最新

学习来源&#xff1a;尚硅谷前端htmlcss零基础教程&#xff0c;2023最新前端开发html5css3视频 文章目录 &#x1f4da;CSS三大属性&#x1f407;层叠性&#x1f407;继承性&#x1f407;优先级 &#x1f4da;CSS常用属性&#x1f407;像素的概念&#x1f407;颜色的表示⭐️表…

【数据挖掘】如何为可视化准备数据

一、说明 想要开始您的下一个数据可视化项目吗&#xff1f;首先与数据清理友好。数据清理是任何数据管道中的重要步骤&#xff0c;可将原始的“脏”数据输入转换为更可靠、相关和简洁的数据输入。诸如Tableau Prep或Alteryx之类的数据准备工具就是为此目的而创建的&#xff0c;…

Android 开发规范(基础版)

背景 项目的代码时间时间很长,经过太多人手,代码的规范性堪忧,目前存在较多的比较自由的「代码规范」,这非常不利于项目的维护,代码可读性也不够高。 分析现有项目的代码的情况,输出的『定制化规范』文档,用于提高代码的可读性和可维护性。 收益 对于个人:帮助团队写「…

IIS 日志分析

Microsoft互联网信息服务&#xff08;IIS&#xff09;服务器&#xff0c;包括Web和FTP&#xff0c;已成为企业必不可少的。但是&#xff0c;IT 安全管理员的工作并不仅仅局限于部署 IIS 服务器。部署后&#xff0c;管理员必须采取安全措施来保护这些服务器。监视 IIS 服务器安全…

Java版知识付费源码 Spring Cloud+Spring Boot+Mybatis+uniapp+前后端分离实现知识付费平台

知识付费平台主要指的是能够通过付费来满足用户知识需求的平台&#xff0c;用户可以通过该平台来消费知识或者开展知识买卖等行为。 此处的平台是一个广义的概念&#xff0c;可以是微信小程序或者论坛&#xff0c;也可以是网页或者手机APP&#xff0c;等&#xff0c;就我国的情…

基于IMX6ULL的AP3216C的QT动态数据曲线图显示

前言&#xff1a;本文为手把手教学 LinuxQT 的典型基础项目 AP3216C 的数据折线图显示&#xff0c;项目使用正点原子的 IMX6ULL 阿尔法( Cortex-A7 系列)开发板。项目需要实现 AP3216C 在 Linux 系统下的驱动&#xff0c;使用 QT 设计 AP3216C 的数据显示页面作为项目的应用层。…

聊聊ChatGPT是如何组织对话的

为什么要组织对话&#xff1f; 总所周知&#xff0c;ChatGPT的训练大致可分为下图中展示的几个阶段&#xff0c;其中&#xff0c;在Pretraining阶段&#xff0c;模型的训练数据是纯文本&#xff0c;目标是根据上文预测下一个token&#xff0c;而在后面的几个阶段中&#xff0c…

【软件测试】selenium中元素的定位

1.元素的定位 不管用那种方式&#xff0c;必须保证页面上该属性的唯一性 1.CSS 定位 CSS(Cascading Style Sheets)是一种语言&#xff0c;它被用来描述HTML 和XML 文档的表现。 CSS 使用选择器来为页面元素绑定属性。这些选择器可以被selenium 用作另外的定位策略CSS的获取可…

scrapy ---分布式爬虫

导模块 pip install scrapy-redis 原来scrapy的Scheduler维护的是本机的任务队列&#xff08;待爬取的地址&#xff09;本机的去重队列&#xff08;放在集合中&#xff09;---》在本机内存中 如果把scrapy项目&#xff0c;部署到多台机器上&#xff0c;多台机器爬取的内容是重…

静态数码管——FPGA

文章目录 前言一、数码管1、数码管简介2、共阴极数码管or共阳极数码管3、共阴极与共阳极的真值表 二、系统设计1、模块框图2、RTL视图 三、源码1、seg_led_static模块2、time_count模块3、top_seg_led_static(顶层文件) 四、效果五、总结六、参考资料 前言 环境&#xff1a; 1、…

我爱学QT--qt的网络编程

学习地址&#xff1a; QT网络编程之TCP通信_哔哩哔哩_bilibili QT网络编程有TCP和UDP。 TCP编程需要用到两个类&#xff1a;QTcpServer和QTcpSocket 本节课目标&#xff1a; 完成一个服务器和一个客户端 首先是经典的几步 先设计ui再设计逻辑实现 多看看写的文件理解吧

【淘宝API接口系列】,商品详情接口响应参数有哪些?响应示例是否满足

商品数据&#xff1a;淘宝提供了商品的基本信息&#xff0c;包括商品名称、描述、规格、价格、销量、库存等信息。此外&#xff0c;也可以通过淘宝提供的API接口来获取商品的图片、评价、物流信息等详细数据。 响应参数 Version: Date:2022-04-04 名称类型必须示例值描述 ite…

数据库小白看这里,这个Oracle数据库知识图谱你值得拥有

2022年前后&#xff0c;墨天轮社区曾陆续推出PostgreSQL知识图谱、MySQL知识图谱&#xff0c;并得到了大家的广泛好评。此后&#xff0c;便有众多朋友对Oracle知识图谱发起不断“催更“。经过近期的内容搜集整合、专家复审与打磨&#xff0c;墨天轮社区正式推出Oracle知识图谱&…

Mac搭建安卓模拟器(支持M1/M2)

引言 最近在研究Vue打包成app&#xff0c;给我的报价器搞一个移动端&#xff0c;奈何没有安卓手机用于测试。所以想到安装一个安卓模拟器。 看了下目前主流的安卓模拟器基本都不支持Mac版本。网易的mumu目前来看还是只支持Intel芯。 1. 简单版&#xff08;仅M系&#xff09;…

目标检测——R-CNN网络基础

目录 Overfeat模型RCNN模型算法流程候选区域生成CNN网络提取特征目标分类&#xff08;SVM&#xff09;目标定位预测过程 算法总结 Fast RCNN模型算法流程ROI Pooling目标分类和回归 模型训练模型总结 Overfeat模型 RCNN模型 算法流程 候选区域生成 CNN网络提取特征 目标分类&am…

掘金量化—Python SDK文档—3.变量约定

目录 Python SDK文档 3.变量约定 3.1 symbol - 代码标识 3.1.1交易所代码 3.1.2交易标的代码 3.1.3symbol 示例 3.1.4期货主力连续合约 3.2mode - 模式选择 3.2.1实时模式 3.2.2回测模式 3.3context - 上下文对象 3.3.1context.symbols - 订阅代码集合 3.3.2context.now - 当…

Docker 镜像解密:分层存储与构建原理多角度解析

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…