[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,一经查实,立即删除!

相关文章

华为OD真题--分苹果-带答案

有A&#xff0c;B两个同学想要分苹果。A的想法是使用二进制进行&#xff0c;1 1相加不进一位&#xff0c;如&#xff08;9 5 1001 101 12&#xff09;。B同学的想法是使用十进制进行&#xff0c;并且进一位。会输入两组数据&#xff0c;一组是苹果总数&#xff0c;一组分别…

代码随香录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;才能用。 只有分了…

ThreadLocal内存泄露原因,如何避免

内存泄露为程序在申请内存后&#xff0c;无法释放已经申请的内存空间&#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; 一.手风琴是什么 二.手风琴在什么时候使用…

Vue 3 功能实现

Vue 3 功能实现 本论旨在研究和分析 Vue 3 的功能实现&#xff0c;深入探讨 Vue 3 作为下一代前端开发框架的特色与创新。论文首先介绍了 Vue 3 的背景和发展历程&#xff0c;随后重点讨论了其引入的核心功能和改进之处&#xff0c;包括响应式系统的全面重构、Composition API…

性能测试之性能问题分析

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

浅谈chatgpt如何改变人们生活

ChatGPT&#xff0c;作为一种自然语言处理模型&#xff0c;确实有潜力改变人们的生活。以下是ChatGPT如何可能改变人们生活的几个方面&#xff1a;1.智能助手和客户支持&#xff1a;ChatGPT可以作为智能助手嵌入到各种应用中&#xff0c;为用户提供即时的帮助和解答问题。在客户…

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

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

spring复习:(41)全注解方式的事务

一、创建事务、数据源相关的配置类&#xff1a; package cn.edu.tju.study.service.transaction;import com.zaxxer.hikari.HikariDataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import or…

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

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

使用matlab中的SVM进行数据回归预测

在MATLAB中使用支持向量机&#xff08;SVM&#xff09;进行数据回归预测&#xff0c;你可以遵循以下步骤&#xff1a; 准备数据集&#xff1a; 将你的特征矩阵X和目标变量向量y加载到MATLAB工作空间中。确保X和y的维度匹配。 拆分数据集&#xff1a; 将数据集划分为训练集和测…

AI编程助手体验

一、背景 最近在接触各种AI插件&#xff0c;用copilot的话要10美元每月&#xff0c;太贵&#xff0c;就下载了清华的CodeGeeX 刚好&#xff0c;有个需求&#xff0c;需要将枚举的所有值&#xff0c;随机组合求和&#xff0c;并返回所有组合之和。 ”假设你有一个名为 enum_v…

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

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

大数据篇Kafka消息队列指定Topic打印Key、Value、Offset和Partition

1、概念简介 说到Apache Kafka消息传递系统时&#xff0c;以下是一些关键概念的解释&#xff1a; Key&#xff08;键&#xff09;&#xff1a;Kafka消息由Key和Value组成。Key是一个可选的字段&#xff0c;它通常用于消息的路由和分区策略。Key的目的是确保具有相同Key的消息…