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

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

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

相关文章

代码随香录day21

235. 二叉搜索树的最近公共祖先 本题思路&#xff1a; 还是要利用二叉搜索树的特性&#xff0c;中序遍历为有序数组。如果pq两个节点都小于root&#xff0c;那么最近公共祖肯定是在他的左子树&#xff0c;如果都大于那么&#xff0c;肯定就在右子树。然后直接return root 代码…

Linux Ubuntu安装RabbitMQ服务

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

【前端知识】React 基础巩固(二十七)——Fragment

React 基础巩固(二十七)——Fragment Fragment Fragment 允许将子列表分组&#xff0c;而无需向 DOM 添加额外节点可以采用语法糖<></>来替代 Fragment&#xff0c;但在需要添加 key 的场景下不能使用此短语 import React, { PureComponent, Fragment } from &q…

Echarts 实现温度计

先上图 <div id="mainOne" style="width: 230px;height:130px;"></div> var dom1 = document.getElementById(mainOne) 核心代码 setTemperature(){var dom1 = document.getElementById(mainOne)var dom2 = document.getElementById(mainTw…

正则表达式与文本处理器

文本处理器三剑客&#xff1a;grep&#xff08;查找&#xff09; sed awk 正则表达式&#xff1a;由一类特殊字符以及文本字符所编写的一种模式&#xff0c;处理文本当中的内容 其中的一些字符不表示字符的字面含义&#xff0c;这些字符表示控制或者通配的功能 通配符&…

在分区工具上,格式化分区和删除分区. 两者有什么不一样吗?

1.格式化分区&#xff1a;就是重建文件系统&#xff0c;等于把目标分区的数据全部清掉。 删除分区&#xff1a;你删除后可以再重新分区&#xff0c;可以分区多个分区&#xff0c;前提是“删除分区”的大小足够大。分了区&#xff0c;还必须格式化&#xff0c;才能用。 只有分了…

掌握无人机遥感数据预处理的全链条理论与实践流程、典型农林植被性状的估算理论与实践方法、利用MATLAB进行编程实践(脚本与GUI开发)以及期刊论文插图制作等

目录 专题一 认识主被动无人机遥感数据 专题二 预处理无人机遥感数据 专题三 定量估算农林植被关键性状 专题四 期刊论文插图精细制作与Appdesigner应用开发 近地面无人机植被定量遥感与生理参数反演 更多推荐 遥感技术作为一种空间大数据手段&#xff0c;能够从多时、多…

LayUi之手风琴的趣味案例

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于LayUi的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.手风琴是什么 二.手风琴在什么时候使用…

性能测试之性能问题分析

开始性能测试前需要了解的内容&#xff1a; 1、项目具体需求。 2、指标&#xff1a;响应时间在多少以内&#xff0c;并发数多少&#xff0c;tps多少&#xff0c;总tps多少&#xff0c;稳定性交易总量多少&#xff0c;事务成功率&#xff0c;交易波动范围&#xff0c;稳定运行时…

【iOS】—— 面向对象,Runtime,ARC等问题总结

对于暑假学习大多数是对之前学习的一个复习&#xff0c;在这里只做对之前学习欠缺知识的补充以及这些知识点涉及的一些问题&#xff0c;从问题入手学习。 文章目录 面向对象1.一个NSObject对象占多少内存&#xff1f;2.对象的isa指针指向哪里&#xff1f;3.OC的类信息存放在哪…

诚迈科技子公司智达诚远精耕智能驾驶,为商用落地注入创新力量

近期&#xff0c;工业和信息化部副部长辛国斌在新闻发布会上表示&#xff0c;将启动智能网联汽车准入和上路通行试点&#xff0c;组织开展城市级“车路云一体化”示范应用&#xff0c;将支持L3级及更高级别的自动驾驶功能商业化应用。根据工信部最新消息&#xff0c;《智能网联…

实际上手体验maven面对冲突Jar包的加载规则 | 京东云技术团队

一、问题背景 相信大家在日常的开发过程中都遇到过Jar包冲突的问题&#xff0c;emm&#xff0c;在最近处理业务需求时我也遇到了不同版本jar包冲突导致项目加载出错的问题。主要是一个完整的项目会不可避免的使用第三方的Jar包来实现功能开发&#xff0c;各种第三方包之间可能…

Python 3 拷贝、浅拷贝、直接引用

诸神缄默不语-个人CSDN博文目录 复杂的以后再补。 总的来说&#xff0c;像常数、字符串这种比较简单的变量无所谓&#xff0c;但是对于一些复杂对象&#xff08;比如list等&#xff09;&#xff0c;如果直接使ba&#xff0c;相当于直接把a的路径给了b&#xff0c;b这个对象的…

day35-Postman/ajax

0目录 1.postman 2.ajax 1.Postman 1.1 定义&#xff1a;postman用于测试http协议接口&#xff0c;无论是开发还是测试人员 1.2 Servlet中的doGet&#xff08;&#xff09;/doPost…

建造者模式-复杂对象的组装与创建

生产一辆车&#xff0c;主要有以下步骤&#xff1a;安装骨架、安装发动机及安装轮胎。这些步骤有指定的执行顺序&#xff0c;步骤缺一不可。 图 传统方案 传统方案存在的问题&#xff1a; 传参不便&#xff0c;虽可在构造函数那传参&#xff0c;但是传参时需要注意参数顺序等…

出租屋智能电表系统

随着科技的不断发展&#xff0c;智能化逐渐成为人们生活中不可或缺的一部分。在房屋租赁市场中&#xff0c;智能电表系统成为越来越多出租屋的标配&#xff0c;为房东和租户带来了便捷和安全。本文将从以下几个方面介绍出租屋智能电表系统的特点和优势。 一、出租屋智能电表系统…

LCD-STM32液晶显示中英文-(7.字模及显示原理)

目录 字模介绍 什么是字模 字模的构成 字模显示原理 字模制作 如何制作字模 字模寻址公式 存储字模文件 字模介绍 什么是字模 有了编码&#xff0c;我们就能在计算机中处理、存储字符了&#xff0c;但是如果计算机处理完字符后直接以编码的形式输出&#xff0c;人类将难…

Flutter:网络图像缓存插件——cached_network_image

前言 为什么要使用这个插件&#xff0c;有什么用呢&#xff1f;毕竟官方提供了Image.network来进行网络图片加载 Image.network和CachedNetworkImage都可以用于在Flutter中加载网络图片&#xff0c;但它们之间有一些区别。 Image.network是Flutter核心库提供的一个构造函数&…

Java性能优化-测试try-catch放在循环内和外的性能对比与业务区别

场景 Java中使用JMH(Java Microbenchmark Harness 微基准测试框架)进行性能测试和优化&#xff1a; Java中使用JMH(Java Microbenchmark Harness 微基准测试框架)进行性能测试和优化_霸道流氓气质的博客-CSDN博客 使用如上方式测试Java中try-catch放在循环内和循环外是否有性…

Unity游戏源码分享-Unity手游射击横版游戏

Unity游戏源码分享-Unity手游射击横版游戏 战斗场景 项目地址&#xff1a; https://download.csdn.net/download/Highning0007/88050256