浏览器V8是怎么进行垃圾回收的

面试相关问题解答

1、浏览器V8是怎么进行垃圾回收的

浏览器的内存占用是有限制的:

复制代码
64位系统:
	物理内存 > 16G =>  最大堆内存限制为4G
	物理内存 < 16G =>  最大堆内存限制为2G
32位系统:
	最大堆内存限制为1G

为什么浏览器要对占用内存做限制呢?

在程序运行的过程中,我们要将不用的内存释放出来,否则复杂的程序会占用很大的内存,导致程序运行缓慢或者卡顿。JavaScript的运行时单线程的,在执行垃圾回收时会阻塞JavaScript应用执行,直到垃圾回收结束才继续执行JavaScript应用逻辑,这种阻塞被称为"全停顿"(stop-the-world)

若V8的内存为1.5G,执行一次小的垃圾回收在50ms以上,做一次非增量级的垃圾回收要1s以上,这样浏览器将在这1s内失去对用户的响应,造成假死想象。如果有动画的话,动画也会收到印象,这样会造成严重的影响。

chrome是很占内存的

chrome很占内存是因为浏览器使用了多进程机制,浏览器的每一个标签页以及扩展都独立占一个进程。访问一个网站,至少要占4个进程:1个浏览器进程,一个GPU进程,一个渲染进程和一个网络进程 ,除此之外,可能还有多个插件组成的进程架构。而最新的Chrome浏览器运行一个网站包括:1个浏览器进程,一个GPU进程,一个网络进程,多个渲染进程和多个插件进程。【多进程架构优点是更稳定、流畅、安全】【缺点是架构复杂(各模块耦合性高、扩展性差)、占用资源高】

我们的javascript是运行在渲染进程中的。

内存空间

在javascript执行过程中,有三种类型的空间:代码空间、栈空间、堆空间

  • 代码空间:存储可执行代码
  • 栈空间:调用栈,用来存储可执行上下文,原始类型的数据值和引用类型的地址都是直接存在栈空间
  • 堆空间:存储引用类型的值
引用类型为什么一定要存在堆空间中,直接存在堆空间中行不行?

答案是不可以,javascript引擎用栈来维护程序执行期间上下文状态,如果栈空间过大,所有的数据都存在栈空间中,会影响到上下文切换的效率,进而影响到整个程序的执行效率

调用栈切换执行上下文状态

栈内存回收

调用一个函数时,V8引擎会创建一个函数的活动对象【执行上下文】并推入调用栈的栈顶。

活动对象包含这个函数的参数、局部变量、返回值

调用完成后,活动对象从栈中弹出,释放内存。继续执行当前执行环境下剩余的代码

当分配的调用栈空间被占满时,会引发"堆栈溢出"问题

一开始调用栈为空,直到函数被调用,便自动的加入调用栈,执行完成,调用栈自动弹出这个函数。依此类推

综上所述,栈内存是随着函数执行完成调用栈弹出活动对象时自动释放的。函数执行完毕,立即释放,节省内存空间。

堆内存回收

堆内存主要用来存放对象和动态数据的地方。是程序对于内存空间最大的一个地方。同时我们常说的垃圾回收就是指堆内存的垃圾回收。

V8使用垃圾回收机制来管理我们的堆内存。简单来说就是释放孤立(非活跃)对象使用的内存。

如何判断非活跃对象
1、引用计数

每当有引用的地方,就加一,去掉引用就减一,这种方式无法解决循环引用的问题,引用计数都无法为0,导致无法GC,所以V8没有使用这种方法

2、可访问性分析法

V8采用了这种方法。

将一个成为GCRoots的对象(在浏览器环境中,GC Roots可以包括:全局的window对象,所有原生dom节点集合等等)做为所有初始存活的对象集合,从这个对象出发,进行遍历,遍历到就认为是可访问的,标记为活动对象,需要保留 ,如果没有访问到,就是非活动对象,可能会被垃圾回收。

在浏览器环境中,GCRoots有很多,通常包含一下几种(实际更多):

  • 全局的window对象
  • 文档DOM树。由可以通过遍历文档到达的所有原生 DOM 节点组成。
  • 存放栈上变量
代际假说

代际假说是垃圾回收的一个关键术语,它有两个特点:

  1. 大部分变量对象的生命周期很短,比如函数内部的变量,块级作用域中的变量。这些代码在函数执行完成就可以清除。
  2. 少量的对象会存活很久,比如全局的window、Dom、全局api等对象

我们将生命周期短的对象叫新生代 ,生命周期长的称为老生代。

对于这两种不同的活动对象,V8分别采用不同的垃圾回收机制,达到高效垃圾回收的目的

副垃圾回收器:主要负责新生代的垃圾回收

主垃圾回收器:主要负责老生代的垃圾回收

新生代 - Scavenger算法

新生代分为两个区域,From区-To区 ,或者称为激活区(new space)-未激活区**(inactive new space)**,这两块区域大小相同,称为Semispace

这是一个牺牲空间换取时间的算法

1、新的对象存放在from-space

2、垃圾回收时,将还活跃的对象复制到to-space

3、清空from-space

4、将from-space和to-space互换,依此类推

scavenger算法需要在每次执行时将存活的对象复制到空闲区域,但是复制需要时间成本,如果新生区空间太大了,复制时间会比较久,所以为了执行效率,新生区空间一般都比较小。

晋升机制
  • 【第一次回收->nursery子代】 =》 【第二次回收 ->intermediate子代】 =》【第三次回收->晋升到老生代】
  • to空间超过25%
老生代

主要使用【标记-清除】和【标记-整理】两个算法

Mark-Sweep【标记清除】

也就是上面提到的可访问性分析

遍历堆中的所有对象,递归调用这组跟元素,标记存活和未存活的对象,标记完成后,将未存活对象进行清除。因为这里都是生命周期长的对象,未存活对象比较少,所以效率比较高。

Mark-Compact【标记整理】

标记清除后,导致不连续的存储空间比较多,产生大量不连续的内存碎片,碎片过多无法分配大对象。

标记整理是将所有存活对象向一端移动,然后直接释放掉端边界以外的内存。从而让活动对象占连续的内存。

垃圾回收引起的性能问题

JavaScript是运行在主线程上的,为了避免JavaScript应用逻辑与垃圾回收产生不一致的冲突,垃圾回收执行时,就会占用JavaScript引擎,正在执行的JavaScript脚本会被暂停。

在V8的分代式垃圾回收中,新生代内存比较小,对应的活动对象也比较少,所以执行速度快,全停顿影响也不大。老生代内存比较大,且活动对象比较多,全堆的活动对象标记、清除、整理耗费的时间就比较长,造成的停顿会比较严重。

如何避免内存泄漏

  • 少创建全局变量

    • 全局变量会在页面关闭时才回收,所以避免创建全局变量,和没有声明的变量(变量提升,变成全局变量)
  • 手动清除定时器

    js 复制代码
    var someResource = getData();
    setInterval(function() {
        var node = document.getElementById('Node');
        if(node) {
            node.innerHTML = JSON.stringify(someResource));
        }
    }, 1000);
    someResource = null; // 定时器依然在引用变量无法回收 
  • 少用闭包

    js 复制代码
    var leaks = (function(){
        var leak = 'xxxxxx';// 闭包中引用,不会被回收
        return function(){
            console.log(leak);
        }
    })()
  • 清除DOM引用

    js 复制代码
    var element = {
      image: document.getElementById('image'),
      button: document.getElementById('button')
    };
    document.body.removeChild(document.getElementById('image'));
    // 如果element没有被回收,这里移除了 image 节点也是没用的,image 节点依然留存在内存中. 

    绑定事件回收

    js 复制代码
    let oDiv = document.querySelector('div');
    oDiv.onclick = function(){
                alert(111111111)
            }
    document.body.removeChild(oDiv);
    oDiv.onclick = null; // 解除事件绑定,触发垃圾回收 
  • 弱引用(WeakMap和WeakSet)

    • 特点:key必须是一个对象,不能是原始值
    • 优点:可以快速被垃圾回收
    • 缺点:不支持迭代以及keys(), values(),和entries()方法。

​

相关推荐
deli00715 分钟前
手机拍灯为什么有一条条黑纹?PWM 240Hz 实测
前端
wangruofeng5 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng5 小时前
给前端开发者的 100 个设计参考网站
前端·设计
星栈8 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技8 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户47949283569159 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马11 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng11 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构
清晨Coding12 小时前
为什么用 React Developer Tools 查看组件树这么乱?
前端·react.js
鶴哥只手遮天13 小时前
《Electron 45.2.3 内核解剖》第 4 章 补丁体系:380 个补丁改了什么
前端·electron