下一个泡泡玛特工程师是谁呢?

收集一些自己用到的prompt

2023-04-05 · 1 min read

机械键盘相关参数

机械键盘相关参数记录

2023-04-05 · 1 min read

Web 前端性能优化

常用的web前端性能优化手段

2023-03-19 · 3 min read

Blender常用快捷键

自己经常用到的一些快捷键记录

2022-12-11 · 1 min read

Docker 常用命令

精简 Docker 常用命令

2022-11-06 · 1 min read

项目管理

项目管理相关知识

2022-11-06 · 1 min read

JS沙箱sandbox的各种实现

我们把Js隔离机制常常称作沙箱

2022-09-18 · 1 min read

Amazing!Solid 比react还react

今天来介绍2个amazing的东东

2022-09-12 · 1 min read

puppeteer应用

使用傀儡师来操作浏览器这个傀儡吧

2022-09-11 · 1 min read

做一个web termianl

前端react, 后端nodejs, 直接可用版web termianl

2022-07-17 · 1 min read

TypeScript里常用的工具类型

经常用到的工具类型,提取抽离出来,供以后复用

2022-06-26 · 1 min read

Vim大法好

想要丢掉鼠标,试试Vim

2022-06-26 · 1 min read

碧血丹心

无论时光如何沾染风霜,也永似红日光

2022-05-10 · 1 min read

chrome V8 引擎中的垃圾回收机制

V8引擎就是nodejs的发动机

2022-03-03 · 1 min read

如何开发一个cli

命令行交互界面是程序员必备的工具,如何开发一个呢?

2022-02-21 · 1 min read

Javascript中的哲学

道生一,一生二,二生三,三生万物

2022-02-20 · 1 min read

React Redux 实现 (Context 版)

React Redux 实现 (Context 版)

2022-02-16 · 1 min read

mobx-react 使用

虽然不常用,但是可以学一下

2022-02-16 · 1 min read

Less中的for和forEach循环

循环的使用是保持代码干燥和避免重复的好方法

2022-02-15 · 1 min read

Webpack Splitchunks 详解

webpack 优化

2022-02-15 · 1 min read

ssh-keygen命令详解

为ssh生成、管理和转换认证密钥

2022-02-15 · 1 min read

使用CURL发送POST请求

curl 是常用的命令行工具,用来请求 Web 服务器。

2022-02-15 · 1 min read

Webpack Plugin 开发

让我们来学一下如何开发一个webpack插件

2022-02-14 · 1 min read

JS 实现两个大数相加?

algo-adding-large-numbers

2022-02-10 · 1 min read

字典树 trie

字典树 trie

2022-02-09 · 1 min read

浏览器原理问题

浏览器原理问题

2022-01-11 · 1 min read

MacBook快速进入一个文件夹目录

mac如何快速进入一个文件夹

2021-10-26 · 1 min read

react合成事件

react-synthetic-event

2021-10-25 · 1 min read

mini webpack实现

通过babel核心来实现迷你版的webpack

2021-10-10 · 1 min read

设计模式

在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于类

2021-10-10 · 1 min read

babel核心

babel核心介绍

2021-10-07 · 1 min read

React 15 和 React 16 的区别

react-15-16

2021-10-06 · 1 min read

React性能优化

浅谈react性能优化的方法

2021-10-05 · 1 min read

交通信号灯实现

如何用js来实现交通信号灯呢

2021-09-25 · 1 min read

内存管理

前端中的内存管理

2021-09-25 · 1 min read

前端安全

前端关于安全方面的知识

2021-09-25 · 2 min read

网络和并发

http各版本对于并发的支持,前端如何控制并发量?

2021-09-25 · 1 min read

跨域方法

总结了9种跨域方法

2021-09-24 · 1 min read

react virtualList 虚拟列表无限滚动实现

用react实现虚拟滚动

2021-09-16 · 1 min read

监控埋点方案

前端监控埋点方案

2021-09-16 · 1 min read

Mini useEffect实现

如何实现useEffect?

2021-09-11 · 1 min read

Mini useState 实现

我们来思考一下useState是怎么实现的呢?

2021-09-11 · 1 min read

React Fiber

react-fiber

2021-09-11 · 1 min read

React class组件和function组件异同

类组件和函数组件有何相同点有何不同点呢?

2021-09-11 · 1 min read

Lodash Get 实现

algo-lodash-get

2021-09-08 · 1 min read

手写reduce实现

algo-reduce

2021-09-08 · 1 min read

Hooks 原理概览

react-hooks

2021-09-05 · 1 min read

Hook原理——状态Hook

react-hook-state

2021-09-05 · 1 min read

手写Mini Redux实现

手写一个简易版的redux实现,包含了核心逻辑

2021-09-04 · 1 min read

legacy和concurrent模式

react-legacy-concurrent

2021-09-01 · 1 min read

react架构

总体 react 的核心可以用 ui=fn(state)来表示 3 大核心对象、3 大核心阶段、2 大工作循环 Scheduler(调度器): 排序优先级,让优先级高的任务先进行 reconcile Reconciler…

2021-09-01 · 2 min read

react核心api和jsx

为什么要有jsx,为什么会有虚拟bom

2021-09-01 · 1 min read

setState是同步的还是异步的

react-setstate-usestate

2021-08-31 · 1 min read

如何使用NodeJs创建HTTP服务?

如何使用NodeJs创建HTTP服务?

2021-08-17 · 1 min read

NodeJS 事件循环模型

nodejs-eventloop

2021-08-11 · 2 min read

Buffer

nodejs中的内存管理

2021-08-10 · 1 min read

微前端解决方案-qiankun

目前国内最好的微前端解决方案-qiankun

2021-08-10 · 1 min read

React Mini版实现(1)

学一门技术最好的方法就是做一个其玩具版的实现,我们来尝试实现一下react和react-dom最简单版本吧

2021-08-04 · 1 min read

brew安装

brew 是 MacOS 上的包管理工具,可以简化 macOS 和 Linux 操作系统上软件的安装。

2021-08-04 · 1 min read

CommonJS简易版实现

CommonJS我们经常用,如何实现一个简易版的commonJS呢?

2021-08-01 · 1 min read

极品透明Dashboard样式分享

一个极品透明Dashboard样式分享

2021-07-30 · 1 min read

Stream

nodejs中的流

2021-07-11 · 1 min read

NodeJS全局对象

JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。

2021-05-18 · 1 min read

如何部署Nodejs服务

如何快速的部署一个Nodejs服务到公网呢?

2021-05-12 · 1 min read

Events

events模块是node的核心模块之一,几乎所有常用的node模块都继承了events模块,比如http、fs等。

2021-05-11 · 1 min read

用JS绘制背景,让CSS直接使用 'background:paint(xxx)'

CSS对象新特性,新的background设置方式

2021-04-30 · 1 min read

Block Formatting Context 块级格式化上下文

可以将BFC看成是元素的一种属性,拥有了这种属性的元素就会使他的子元素与世隔绝,不会影响到外部其他元素

2021-04-17 · 1 min read

如何使用Nodejs来创建一个TCP/UDP服务?

如何使用Nodejs来创建一个TCP/UDP服务?

2021-04-17 · 1 min read

Mac使用tree生成目录结构

程序员经常会有需求,需要列出项目的结构树。Mac或者Linux下可以使用tree列出项目结构

2021-04-04 · 1 min read

常见算法

一些比较常见算法

2021-03-20 · 1 min read

前端缓存

对于性能优化离不开缓存

2021-02-28 · 1 min read

前端答疑

一些比较常见的问题

2020-09-02 · 1 min read

Nodejs 网络 & HTTP

nodejs-network

2020-08-14 · 1 min read

JavaScript AST 抽象语法树

源代码的抽象语法结构的树状表现形式

2020-08-02 · 1 min read

nodejs里面向切面编程的一种范式

在一些场景下我们可能需要一种面向切面的编程方式

2020-08-01 · 1 min read

各种JS模块化特性

AMD、CMD、CJS、ESM

2020-05-30 · 1 min read

手写Ajax实现

使用HMR一步步实现Ajax

2020-05-30 · min read

手写PromiseA+实现

如何自己实现promiseA+规范,手写一个promise实现

2020-04-30 · 1 min read

debug和内存泄露

nodejs的debug方法

2020-04-08 · 1 min read

Javascript prototype 原型链

js-prototype

2019-09-07 · 1 min read

this指针、作用域

this是在执行时动态读取上下文决定的,不是在定义时决定

2019-06-14 · 1 min read

call、apply、bind的极简实现

使用symbol实

2019-06-03 · 1 min read

CSS联合选择器区分列表元素个数不同所要求的不同样式

对于列表,在有些时候针对于不同个数的item会有不同的显示,比如col份数,用js固然可以,是否可以用css更简便的实现呢?

2019-04-30 · 1 min read

TypeScript基础

介绍TypeScript基础知识

2019-04-30 · 1 min read

JS中的变量提升

为什么js当时要这样设计

2019-03-07 · 1 min read

ES6之Class

关于ES6里的class, 我们有什么不知道的事?

2019-03-01 · 1 min read

HTTP详解

HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传输协议

2019-02-17 · 2 min read

极简代码实现节流Throttle和防抖Debounce

使用各9行代码实现节流和防抖函数

2019-01-30 · 1 min read

Javascript 事件循环 EventLoop

js-eventloop

2018-09-07 · 1 min read

POST和GET区别

POST和GET区别是什么呢?

2018-08-14 · 1 min read

正则表达式

正则表达式一锅端

2018-07-30 · 1 min read

cloneDeep 深克隆实现

algo-clonedeep

2017-09-07 · 1 min read

webpack

engin-webpack

2017-09-07 · 1 min read

http1.1和http2.0有什么区别

http1.1和http2.0有什么区别

2017-06-14 · 1 min read

手写EventEmitter事件巴士

咱们来手写实现一个EventEmitter事件巴士

2017-01-10 · 1 min read

OOP 面向对象编程

对象是什么?为什么要面向对象?

2016-05-08 · 1 min read
Stay hungry & Stay foolish
战歌
16
The Reluctant Warrior
Immediate Music
To Glory
Two Steps From Hell
Victory
Two Steps From Hell
Empire of Angels
Thomas Bergersen
Serenata Immortale
Immediate Music
Cornfield Chase
Hans Zimmer
Tennessee
Hans Zimmer
He's a Pirate
Martin Ermen
Rise
Hans Zimmer
On Thin Ice
Hans Zimmer
Angels Will Rise
Twisted Jukebo
When It All Falls Down
Audiomachine
Icarus
Ivan Torrent
Star Sky - Instrumental
Two Steps From Hell
亡灵序曲
L
Up Is Down
Hans Zimm
回到首页

chrome V8 引擎中的垃圾回收机制

袁官东
March 3rd, 2022 · 1 min read
图:Peter Tarka

引言

JavaScript 可以高效地运行在浏览器和 Nodejs 这两大宿主环境中,是因为背后有强大的 V8 引擎在为其保驾护航,甚至成就了 Chrome 在浏览器中的霸主地位。

正是因为有 V8 引擎的存在才会有前端今天的繁荣生态

感谢V8 🙇

V8 引擎为了追求极致的性能和更好的用户体验,为我们做了太多太多。

V8 引擎努力降低整体的内存占用和提升到更高的运行性能。

我们来看一下 V8 引擎中的垃圾回收机制

引用法

概念

判断一个对象的引用数,引用数为 0 就回收,引用数大于 0 就不回收

1// 在堆中创建一个对象,foo是这个对象的引用
2let foo = { name: "foo" };
3// 把引用地址改为null, { name: "foo" }对象在堆中没有被栈中引用,那么这块堆中的内存就会被回收
4foo = null;

缺陷

如果对象之间有相互引用关系,并且引用关系一直存在,那么就不会被回收,从而造成内存泄漏

1function fn() {
2 const obj1 = {};
3 const obj2 = {};
4 obj1.a = obj2;
5 obj2.a = obj1;
6}
7fn();

标记法

标记法就是,将可达的对象标记起来,不可达的对象当成垃圾回收。

就是从初始的根对象(window 或者 global)的指针开始,向下搜索子节点,子节点被搜索到了,说明该子节点的引用对象可达,并为其进行标记,然后接着递归搜索,直到所有子节点被遍历结束。那么没有被遍历到节点,也就没有被标记,也就会被当成没有被任何地方引用,就可以证明这是一个需要被释放内存的对象,可以被垃圾回收器回收。

JavaScript 内存管理

1、分配给使用者所需的内存

2、使用者拿到这些内存,并使用内存

3、使用者不需要这些内存了,释放并归还给系统

1var name = "john";
2var obj = { name: "john" };

name 和 obj 是使用者

  • 基础数据类型:拥有固定的大小,值保存在栈内存里,可以通过值直接访问

  • 引用数据类型:大小不固定(可以加属性),栈内存中存着指针,指向堆内存中的对象空间,通过引用来访问

  • 由于栈内存所存的基础数据类型大小是固定的,所以栈内存的内存都是操作系统自动分配和释放回收的

  • 由于堆内存所存大小不固定,系统无法自动释放回收,所以需要 JS 引擎来手动释放这些内存

分代回收

在 JavaScript 中,对象存活周期分为两种情况

  • 存活周期很短:经过一次垃圾回收后,就被释放回收掉
  • 存活周期很长:经过多次垃圾回收后,他还存在,赖着不走

V8 将堆分为两个空间,一个叫新生代,一个叫老生代,新生代是存放存活周期短对象的地方,老生代是存放存活周期长对象的地方

新生代通常只有 1-8M 的容量,而老生代的容量就大很多了。对于这两块区域,V8 分别使用了不同的垃圾回收器和不同的回收算法,以便更高效地实施垃圾回收

  • 副垃圾回收器 + Scavenge 算法:主要负责新生代的垃圾回收
  • 主垃圾回收器 + Mark-Sweep && Mark-Compact 算法:主要负责老生代的垃圾回收

新生代

在 JavaScript 中,任何对象的声明分配到的内存,将会先被放置在新生代中,而因为大部分对象在内存中存活的周期很短,所以需要一个效率非常高的算法。

在新生代中,主要使用 Scavenge(清道夫)算法进行垃圾回收,Scavenge 算法是一个典型的牺牲空间换取时间的复制算法,在占用空间不大的场景上非常适用。

Scavange 算法将新生代堆分为两部分,分别叫 from-space 和 to-space,工作方式也很简单,就是将 from-space 中存活的活动对象复制到 to-space 中,并将这些对象的内存有序的排列起来,然后将 from-space 中的非活动对象的内存进行释放,完成之后,将 from space  和 to space 进行互换,这样可以使得新生代中的这两块区域可以重复利用。

  • 1、标记活动对象和非活动对象
  • 2、复制 from-space 的活动对象到 to-space 中并进行排序
  • 3、清除 from-space 中的非活动对象
  • 4、将 from-space 和 to-space 进行角色互换,以便下一次的 Scavenge 算法垃圾回收

通过可达性进行活动对象的判断

在新生代中,还进一步进行了细分。 分为 nursery(托儿所小班) 子代和 intermediate(托儿所中班) 子代两个区域,一个对象第一次分配内存时会被分配到新生代中的 nursery 子代,如果经过下一次垃圾回收这个对象还存在新生代中,这时候我们将此对象移动到 intermediate 子代,在经过下一次垃圾回收,如果这个对象还在新生代中,副垃圾回收器会将该对象移动到老生代中,这个移动的过程被称为晋升

很形象,顽固的老油条我来单独处理你,别在托儿所上了,让小学老师来收拾你 🤣 😎

老生代

新生代空间的对象,身经百战之后,留下来的老对象,成功晋升到了老生代空间里,由于这些对象都是经过多次回收过程但是没有被回收走的,都是一群生命力顽强,存活率高的对象,所以老生代里,回收算法不宜使用 Scavenge 算法,为啥呢,有以下原因:

Scavenge 算法是复制算法,反复复制这些存活率高的对象,没什么意义,效率极低 Scavenge 算法是以空间换时间的算法,老生代是内存很大的空间,如果使用 Scavenge 算法,空间资源非常浪费,得不偿失啊。。

所以老生代里使用了 Mark-Sweep 算法(标记清理)和 Mark-Compact 算法(标记整理)

套路和之前一样 Mark-Sweep 算法(标记清理)

  • 标记阶段:对老生代对象进行第一次扫描,对活动对象进行标记
  • 清理阶段:对老生代对象进行第二次扫描,清除未标记的对象,即非活动对象

Mark-Compact 算法(标记整理) 把内存空间整理一下,就像文件夹按网格排序一样

全停顿(Stop-The-World)

说完V8的分代回收,咱们来聊聊一个问题。JS代码的运行要用到JS引擎,垃圾回收也要用到JS引擎,那如果这两者同时进行了,发生冲突了咋办呢?答案是,垃圾回收优先于代码执行,会先停止代码的执行,等到垃圾回收完毕,再执行JS代码。这个过程,称为全停顿 由于新生代空间小,并且存活对象少,再配合Scavenge算法,停顿时间较短。但是老生代就不一样了,某些情况活动对象比较多的时候,停顿时间就会较长,使得页面出现了卡顿现象。

Orinoco(奥里诺科)优化

orinoco为V8的垃圾回收器的项目代号,为了提升用户体验,解决全停顿问题,它提出了增量标记、懒性清理、并发、并行的优化方法。

增量标记(Incremental marking)

咱们前面不断强调了先标记,后清除,而增量标记就是在标记这个阶段进行了优化。我举个生动的例子:路上有很多垃圾,害得路人都走不了路,需要清洁工打扫干净才能走。前几天路上的垃圾都比较少,所以路人们都等到清洁工全部清理干净才通过,但是后几天垃圾越来越多,清洁工清理的太久了,路人就等不及了,跟清洁工说:“你打扫一段,我就走一段,这样效率高”。 大家把上面例子里,清洁工清理垃圾的过程——标记过程,路人——JS代码,一一对应就懂了。当垃圾少量时不会做增量标记优化,但是当垃圾达到一定数量时,增量标记就会开启:标记一点,JS代码运行一段,从而提高效率

非常像react fiber架构,任务纤维化

惰性清理(Lazy sweeping)

上面说了,增量标记只是针对标记阶段,而惰性清理就是针对清除阶段了。在增量标记之后,要进行清理非活动对象的时候,垃圾回收器发现了其实就算是不清理,剩余的空间也足以让JS代码跑起来,所以就延迟了清理,让JS代码先执行,或者只清理部分垃圾,而不清理全部。这个优化就叫做惰性清理 整理标记和惰性清理的出现,大大改善了全停顿现象。但是问题也来了:增量标记是标记一点,JS运行一段,那如果你前脚刚标记一个对象为活动对象,后脚JS代码就把此对象设置为非活动对象,或者反过来,前脚没有标记一个对象为活动对象,后脚JS代码就把此对象设置为活动对象。总结起来就是:标记和代码执行的穿插,有可能造成对象引用改变,标记错误现象。这就需要使用写屏障技术来记录这些引用关系的变化

并发(Concurrent)

并发式GC允许在在垃圾回收的同时不需要将主线程挂起,两者可以同时进行,只有在个别时候需要短暂停下来让垃圾回收器做一些特殊的操作。但是这种方式也要面对增量回收的问题,就是在垃圾回收过程中,由于JavaScript代码在执行,堆中的对象的引用关系随时可能会变化,所以也要进行写屏障操作。

并行

并行式GC允许主线程和辅助线程同时执行同样的GC工作,这样可以让辅助线程来分担主线程的GC工作,使得垃圾回收所耗费的时间等于总时间除以参与的线程数量(加上一些同步开销)。

总结

我们在写react时有一句俗语,“遇事不决加一层”, 同样,在性能优化方面,总是绕不开 “遇事不决加一个线程” ,并发、协程的解决问题, 纤维化任务等。

程序世界果然好多套路都是相通的!

More articles from Favori 重剑

如何开发一个cli

命令行交互界面是程序员必备的工具,如何开发一个呢?

February 21st, 2022 · 1 min read

Javascript中的哲学

道生一,一生二,二生三,三生万物

February 20th, 2022 · 1 min read
© 2016–2023 Favori 重剑
Link to $https://github.com/yuanguandongLink to $https://favori.zcool.com.cn/Link to $https://codepen.io/favori