js的堆栈和垃圾回收机制(gc)

news2024/12/26 11:48:30

js的堆栈和垃圾回收机制(gc)

本文目录

    • js的堆栈和垃圾回收机制(gc)
      • 堆栈
      • 深拷贝和浅拷贝
        • 实现深拷贝
      • 垃圾回收机制
        • 栈溢出
        • 概念
        • 垃圾产生
        • 算法策略
        • V8引擎的优化
          • 新生代:Scavenge 算法
          • 老生代:标记-清除-整理 算法

堆栈

在js引擎中对变量的存储主要有两种位置

栈内存(stack)
堆内存(heap)

栈内存(stack):基本数据类型(Number、String 、Boolean、Null和Undefined)存储在栈中,按值访问,栈会自动分配内存空间,自动释放,存放简单类型,简单的数据段,占据固定大小的空间。
堆内存(heap):引用数据类型(Object 、Array 、Function等)的具体内容存储在堆中,其在堆内存中的引用地址(指针)存储在栈中,按引用访问(访问引用类型的数据时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据)动态分配的内存,大小不定也不会自动释放,存放引用类型

简单数据类型是在栈里直接开辟一个空间存放它的值
复杂数据类型则是在栈里开辟一个空间存放它内容的地址(十六进制),这个地址指向存放在堆里的内容

为什么基本数据类型存储在栈中,引用数据类型存储在堆中?
JavaScript引擎需要用栈来维护程序执行期间的上下文的状态,如果栈空间大了的话,所有数据都存放在栈空间里面,会影响到上下文切换的效率,进而影响整个程序的执行效率。

  • 基本类型
var a = 10; // 栈内存:a -> 10
var b = a; // 栈内存:b -> 10
b = 20; // 栈内存:b -> 20
// a仍然是10,b现在是20。

// 函数里的形参如果是简单数据类型,它的值也是存储在栈里的
function fn(y){
  y++; // 栈内存(函数作用域内):y -> 11
  console.log(y); // 输出:11
}
var x = 10; // 栈内存:x -> 10
fn(x); // 将x的值复制给a,栈内存(函数作用域内):y -> 10
console.log(x); // 输出:10
// x仍然是10,因为函数内部修改y的值并不影响x的值。

  • 引用类型
var a = {
  name: '小红',
  age: 18
}
console.log(a)

var b = a;
b.age = 20
console.log(a)
console.log(b)

深拷贝和浅拷贝

  • 浅拷贝:只是把存储在栈里的内容进行了赋值。
  • 深拷贝:就是复杂数据类型a给另一个复杂数据类型b赋值时,跳过栈,直接寻找a在堆里的内容进行拷贝,并在堆里为b开辟新的空间存储地址。

实现深拷贝

  • JSON.parse(JSON.stringify(a))
var b = JSON.parse(JSON.stringify(a));

对象的属性值不能是 undefined、symbol、函数、日期和正则

  • Object.assign(obj1, obj2)
let obj = {
	id: 1,
	name: '张三',
	age: 10,
}
let newObj = Object.assign({}, obj)

只有一级属性为深拷贝,二级属性后就是浅拷贝

二级属性例子

let obj = {
    id: 1,
    name: '张三',
    age: 10,
    friends: ['李四', '王五'],
    address: {
        street: '某某街',
        number: 100
    }
};
let newObj = Object.assign({}, obj);

在栈内存中,obj和newObj的friends和address属性并不直接存储这些对象的值,而是存储了指向堆内存中相应数据的引用地址(也就是Address3和Address4)

Address3和Address4是表示friends数组和address对象在堆内存中的地址。这两个属性是复杂数据类型(即它们是对象),因此他们的值被存储在堆内存中
这是因为JavaScript对于复杂数据类型(例如对象、数组)总是通过引用来处理的,而非直接存储其值。这就意味着,当你修改了obj的friends或address属性时,由于newObj的这些属性引用的是同一块堆内存,因此newObj中相应的属性值也会随之改变。这就是为什么Object.assign()方法只能实现一层深拷贝的原因

  • 扩展运算符
var obj = {
  a: 1,
  b: 2
}
var obj1 = {…obj}
// 数组
let newArr = [...arr]

只有一级属性为深拷贝,二级属性后就是浅拷贝

  • 数组使用数组方法进行深拷贝
var arr1 = [1, 2, 3, 4]
var arr2 = arr1.concat()
var arr3 = arr1.slice(1)

只有一级属性为深拷贝,二级属性后就是浅拷贝,如[1,2,3,[1,2,3]]

垃圾回收机制

栈溢出

(function foo() {
    foo()
})()

栈虽然很轻量,在使用时创建,使用结束后销毁,但是不是可以无限增长的,被分配的调用栈空间被占满时,就会引起”栈溢出“的错误

Maximum call stack size exceeded

概念

在 JavaScript 内存管理中有一个概念叫做 可达性,就是那些以某种方式可访问或者说可用的值,它们被保证存储在内存中,反之不可访问则需回收

垃圾回收过程是不实时进行的,因为JavaScript是一门单线程的语言,每次执行垃圾回收,会使程序应用逻辑暂停,执行完垃圾后回收在执行应用逻辑,这种行为称为全停顿,所以一般垃圾回收会在cpu闲时进行

垃圾产生

程序的运行需要内存,只要程序提出要求,操作系统或者运行时就必须提供内存,那么对于持续运行的服务进程,必须要及时释放内存,否则,内存占用越来越高,轻则影响系统性能,重则就会导致进程崩溃

先声明了一个Person变量,它引用了对象

{name: "江流",age: 20}

接着又将这个Person变量指向了另一个对象

{name: "心猿", age: 5000}

那么之前被引用的对象,现在就成了无用对象,也永远无法使用操作该对象,这种对象就是一个垃圾

算法策略

V8引擎的优化

  • 新生代内存是临时分配的内存,存活时间短,新生对象或只经过一次垃圾回收的对象
  • 老生代内存是常驻内存,存活时间长,经历过一次或多次垃圾回收的对象
新生代:Scavenge 算法

新生代对象是通过一个名为 Scavenge 的算法进行垃圾回收,在 Scavenge 算法 的具体实现中,主要采用了一种复制式的方法即 Cheney 算法

Cheney算法 中将堆内存一分为二,一个是处于使用状态的空间 使用区(from),一个是处于闲置状态的空间 空闲区(to)

  • 新的对象会首先被分配到对象(from)空间,当对象区域快写满时,就需要执行一次垃圾清理操作。
  • 当进行垃圾收回时,先将 from 空间中存活的对象复制到空闲(to)空间进行保存,对未存活的空间进行回收。
  • 复制完成后,对象空间和空闲空间进行角色调换,空闲空间变成新的对象空间,原来的对象空间则变成空闲空间。
  • 这样就完成了垃圾对象的回收操作,同时这种角色调换的操作能让新生代中的这两块区域无限重复使用下去

缺点:堆使用效率低下

当一个对象在两次变换中还存在时,就会从 新生代区 晋升到 老生代区,这一过程被称为对象晋升策略

老生代:标记-清除-整理 算法

复制大对象所花费的时间长,执行效率并不高

  • 标记-清除(Mark-Sweep)

    分为标记和清除两个阶段。标记阶段会遍历堆中所有的对象,并对存活的对象进行标记,清除阶段则是对未标记的对象进行清除

  • 标记-整理(Mark-Compact)

    经过标记清除之后的内存空间会生产很多不连续的碎片空间,这种不连续的碎片空间中,在遇到较大的对象时可能会由于空间不足而导致无法存储。为了解决内存碎片的问题,需要使用另外一种算法 - 标记-整理(Mark-Compact)。标记整理对待未存活对象不是立即回收,而是将存活对象移动到一边,然后直接清掉端边界以外的内存

  • 增量标记

    为了避免垃圾回收时间过长影响其他程序的执行,V8将标记过程分成一个个小的子标记过程,同时让垃圾回收和JavaScript应用逻辑代码交替执行,直到标记阶段完成,这个过程为增量标记算法

    把垃圾回收这个大的任务分成一个个小任务,穿插在 JavaScript任务中间执行

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

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

相关文章

厚积薄发丨美格智能子公司众格智能荣获“张江之星”“闵行区企业技术中心”双项企业授牌

5月23日下午,2023年闵行科技节开幕式在大零号湾科创大厦举行。在开幕式“创蝶变智造”板块,美格智能技术股份有限公司全资子公司众格智能科技(上海)有限公司(以下简称:众格智能)入选“张江之星”…

加速数实融合,数据交易3.0模式上新

数据交易市场将迎来真正的突破? 目前看的确如此。随着去年底“数据二十条”的颁布,业界普遍认为数据基础制度将加速走向落地与完善,数据要素化今年有望迎来全面提速,将极大促进数据交易市场走向规模化。 IDC预测,到2…

QMI8658 - 姿态传感器学习笔记 - Ⅲ

文章目录 1.复位1.1 上电复位:1.2 推荐工作条件 2. 校准(COD)2.1 校准步骤2.2 校准注意事项:2.3 校准状态指示2.4 校准参数更新 3. 自检3.1 加速度计自检3.2 陀螺仪自检 4. Ctrl94.1 写Ctrl94.2 读Ctrl94.3 Ctrl9详细命令说明 5. 中断5.1 同步采样模式5.…

直接缓存访问DCA

直接缓存访问DCA:网卡原本DMA写是将接收到的数据帧写入系统内存,DCA机制是网卡DMA写输入的数据能直接发送到属于CPU内部的L2高速缓存中,从而提高网络IO的性能。 设备驱动程序要初始化网卡的DCA功能,将CPU ID号(通过获取…

CentOS安装Whisper | RedHat安装Whisper | 服务器上安装Whisper

1、升级Python版本(可选) CentOS 7.9 系统默认安装的Python版本有两个,如果输入python -V我们看到的是python2的版本信息,如果我们输入python3 -V我们看到的是python3的版本信息。 由于whisper要求使用的是python版本是在3.8到3…

Linux(Ubuntu)安装和使用Edge浏览器

全文内容完全由AI创作,并通过人工核验,感谢各位的阅读。 Microsoft Edge是一款现代化的浏览器,它拥有众多功能和强大的性能,为用户带来更加流畅的浏览体验。 Edge最近推出了分屏功能,支持一个窗口同时显示两个选项卡&…

MySQL基础 — 多表查询以及事务管理

文章目录 MySQL基础 — 多表查询以及事务管理一、多表查询1.1 对应关系1.2 准备数据1.3 概述1.4 内连接1.5 外连接1.6 自连接1.7 联合查询 union1.8 子查询1.8.1 标量子查询1.8.2 列子查询1.8.3 行子查询1.8.4 表子查询 二、事务2.1 简介2.2 操作演示2.3 控制事务2.3.1 控制事务…

深入浅出Vite:深入理解 Rollup 的插件机制

上一节我们学会了 Rollup 构建工具的使用,相信你已经对 Rollup 的基础概念和使用有了基本的掌握。同时我们也知道,仅仅使用 Rollup 内置的打包能力很难满足项目日益复杂的构建需求。对于一个真实的项目构建场景来说,我们还需要考虑到模块打包…

如何通过CRM系统做好客户的分级分类

随着市场竞争的不断加剧,尤其是以客户为中心时代的到来,企业越来越注重客户的管理和服务。而CRM系统,作为企业客户管理的重要工具,其核心任务是对客户进行分级分类,以便更好地为客户提供定制化的服务。 客户之间的价值…

在 Docker 容器中安装 nvm

目录 前言一、步骤1.下载nvm安装脚本2.激活nvm3.安装Node.js4.验证 Node.js 是否安装成功5.如果需要安装特定版本的 Node.js,可以使用以下命令6.如果需要使用特定版本的 Node.js,可以使用以下命令6.验证特定版本的 Node.js 是否安装成功 总结 前言 提示…

关于 arduino 中的 random()和randomSeed()的使用

0、random() 函数: 用于生成一个指定范围内的伪随机整数,可以使用该函数在程序中产生不同的随机数。其语法如下: random(min, max) 其中,min 表示随机数的最小值,max 表示随机数的最大值(不包括该…

介绍几种常见的运维发布策略

随着Devops的发展,为了提高运维发布的成功率,探索出了多种发布策略。本文简单介绍几种常见发布策略, 以及它们适用的场景和优缺点。 第一种,停机发布 这是最早的一种发布策略,停机发布会在发布以前关闭服务,停止用户…

benders分解算法 逻辑思路整理(加星)

Benders decomposition 目录 1.benders的分类 2. 经典的benders分解 2.1 经典的benders分解注意点 2.2 benders分解的核心——子问题和对偶子问题的分析 benders分解本质是: (1)将问题分解为松弛主问题和子问题 (2&#xff0…

支付宝SDK接口调试- cpolar内网穿透工具实现公网地址调试(1)

文章目录 1.测试环境2.本地配置3. 内网穿透3.1 下载安装cpolar内网穿透3.2 创建隧道 4. 测试公网访问5. 配置固定二级子域名5.1 保留一个二级子域名5.2 配置二级子域名 6. 使用固定二级子域名进行访问 转发自cpolar内网穿透的文章:Java支付宝沙箱环境支付&#xff0…

Cisco Identity Services Engine (ISE) 3.2 Patch2 发布 - 思科身份服务引擎

Cisco Identity Services Engine (ISE) 3.2 Patch2 发布 - 思科身份服务引擎 请访问原文链接:https://sysin.org/blog/cisco-ise-3/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org 工作场所零信任安全的核心所在 …

FL Studio水果软件好用吗?对电脑硬件环境有哪些需求

如果你打算将来朝着艺术和音乐方向发展,那么学习音乐理论和音乐制作就是一门基础课了。 实践才是检验学习效果途径,在我们日常的练习中,一款功能强大且易学的音乐制作工具是少不了的。在没有实际体验过各个音乐制作工具的功能前,…

SpringCloud概论

目录 1.实现微服务需要解决的问题 2.解决这些问题需要的技术栈 3.spring cloud 4.Netflix和Alibaba 5.springCloud和doubbo 6.版本适配 7.停更 1.实现微服务需要解决的问题 马丁福勒于2014年在一篇文章中提出微服务架构,原文地址如下: Microser…

C++STL— stack和queue的介绍及使用

stack stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其只能从容器的一端进行元素的插入与提取操作。 stack的定义方式 方式一&#xff1a; 使用默认的适配器定义栈。 stack<int> st1; 方式二&#xff1a; 使用特定的适配器定义…

病毒分析丨plubx

作者丨黑蛋 一、基本信息 文件名称 00fbfaf36114d3ff9e2c43885341f1c02fade82b49d1cf451bc756d992c84b06 文件格式 RAR 文件类型(Magic) RAR archive data, v5 文件大小 157.74KB SHA256 00fbfaf36114d3ff9e2c43885341f1c02fade82b49d1cf451bc756d992c84b06 SHA1 1c251974b2e…

CTF 2023 三道pwn题

作者丨selph appointment_book 程序信息 程序保护信息&#xff1a; ➜ HeroCTF checksec appointment_book [*] /home/selph/ctf/HeroCTF/appointment_book Arch: amd64-64-little RELRO: No RELRO Stack: Canary found NX: NX enabled …