前端提升必备知识:浏览器的进程与线程(5.8k小长文)

464 阅读19分钟

前言

作为一名前端开发者,我们几乎天天要和浏览器打交道,那么了解浏览器(主要是 Chrome)的工作机制,将能使我们的技术得到极大提升,但是在这之前,我们必须要先搞清楚两个概念:进程(Process)与线程(Thread)

一. 进程与线程的基本概念

  • 进程: 进程是计算机中的程序关于某数据集合上的一次运行活动,是进程是操作系统进行资源分配(如内存、文件句柄等)的基本单位。简单来说,一个进程就是一个正在运行的程序实例,就像 Chrome浏览器 运行时,你能在任务管理器中看到 Chrome浏览器 的进程。

    进程示意图.jpg

    • 进程拥有独立的内存空间,包含了程序运行所需的各种资源,如代码、数据、文件句柄等。
    • 不同进程间资源不共享,通信需通过特定机制(如 IPC,进程间通信)。
  • 线程: 线程是进程内的一个执行单元(依赖进程存在),是操作系统能够进行运算调度的最小单位

    • 一个进程可以包含多个线程,一般为多线程并行处理(显著提升效率)。
    • 它自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可以与同属一个进程的其他线程共享进程所拥有的全部资源
    • 进程中的任一线程触发致命错误(如内存访问越界、栈溢出),都会导致整个进程崩溃。
    • 当进程结束时,其内部的所有线程也会随之结束,其所占内存也会被收回。

进程与线程之间的关系

  • 打个比方便于理解:
    • 我们的操作系统就像一个大公司,为了完成一个项目(可以看作是运行一个程序),公司成立了一个项目组(项目组就可以看作是进程),而项目组内可以招募多个成员进行项目开发(成员就可以看作是线程)。比起成立一个项目组,项目组内部的人事变动要容易的多(线程的创建和销毁成本低)。
    • 各个项目组之间沟通比较困难,通常需要上层领导协调(进程间通信需要特定机制),而项目组内部的成员沟通起来就相对方便。这个项目组的人员每一个都是项目必须的,少一个人项目都将无法进行下去(进程任一线程出错导致进程崩溃)。在开发过程中,项目组内成员共享项目组的资源(线程共享进程资源),并且成员们同时进行项目开发(多线程并行处理)。当项目组解散时,其内部的所有成员也会随之失业,很无良有没有啊(线程随着其进程结束而结束)。

进程与线程的区别

  • 进程可以看做独立应用,线程不能。
  • 资源: 进程是CPU资源分配的最小单位(是能拥有资源和独立运行的最小单位);线程是CPU调度的最小单位(线程是建立在进程的基础上的⼀次程序运⾏单位,⼀个进程中可以有多个线程)。
  • 通信⽅⾯: 线程间可以通过直接共享同⼀进程中的资源,通信更高效,⽽进程通信需要借助进程间通信
  • 调度: 进程切换比线程切换的开销要大。线程是CPU调度的基本单位,线程的切换不会引起进程切换,但某个进程中的线程切换到另⼀个进程中的线程时,会引起进程切换。
  • 系统开销: 由于创建或撤销进程时,系统都要为之分配或回收资源,如内存、I/O 等,其开销远大于创建或撤销线程时的开销。同理,在进⾏进程切换时,涉及当前执⾏进程 CPU 环境还有各种各样状态的保存及新调度进程状态的设置,⽽线程切换时只需保存和设置少量寄存器内容,开销较小。

二. 浏览器进程架构

上面说的进程与线程,其实更偏向操作系统方面的(当然浏览器作为运行在操作系统上的进程,也遵守这个规则),接下来我将向你介绍浏览器方面的进程架构。

(一)早期浏览器进程架构

早期的浏览器,比如 IE6,采用的是单进程架构,所有功能模块(UI 交互、页面渲染、JS执行、网络请求、插件运行等)都在一个进程内完成。 单进程浏览器架构.webp(原图来自极客时间《浏览器工作原理与实践》
虽然这种“大包大揽”的模式看起来很省事,但是这种架构存在很多问题:

1. 稳定性极差:局部故障导致全局崩溃

上面的基本概念环节中,我们说过 “进程中的任一线程出错,都会导致整个进程崩溃”,在这种单进程的架构中,所有标签页、插件、扩展都共享同一内存空间和执行环境。

  • 单个组件崩溃牵连整个浏览器: 只要其中一个组件出现致命错误(如页面脚本死循环、插件内存溢出、渲染引擎崩溃),就会导致整个进程崩溃,所有标签页、浏览器窗口全部关闭,用户正在进行的操作(如表单填写、视频播放)会完全丢失。
  • 无隔离保护机制: 进程内的任何模块都没有 “安全边界”,一个标签页的异常行为(如无限递归调用导致栈溢出)会直接耗尽进程资源,引发浏览器整体无响应。

2. 安全性薄弱:恶意代码易窃取全局资源

上面的基本概念环节中,我们说过 “进程中的线程共享进程所拥有的全部资源”,一旦线程中出现恶意代码,那么浏览器的核心资源将完全不设防

  • 沙箱机制失效: 单进程架构无法实现严格的 “沙箱隔离”,页面脚本、插件或恶意代码可以直接访问浏览器的核心资源(如本地文件、Cookie、用户隐私数据)。例如,恶意 Flash 插件可利用进程内权限读取用户硬盘文件,或篡改其他标签页的 DOM 内容。
    • 补充:沙箱机制(sandbox)
      • 你可以把沙箱看成是操作系统给进程上了一把锁,沙箱里面的程序可以运行,但是不能在你的硬盘上写入任何数据,也不能在敏感位置读取任何数据,例如你的文档和桌面。Chrome 把插件进程和渲染进程锁在沙箱里面,这样即使在渲染进程或者插件进程里面执行了恶意程序,恶意程序也无法突破沙箱去获取系统权限。单进程架构无法实现严格的 “沙箱隔离”。
  • 权限失控: 所有功能模块共享同一套系统权限,一旦某个模块被黑客攻破(如通过漏洞注入恶意代码),攻击者就能获得进程的全部权限,进而控制整个浏览器,甚至攻击操作系统。

3. 性能瓶颈突出:资源竞争导致卡顿

上面的基本概念环节中,我们说过 “线程之间通信更为高效,线程共享进程内的全部资源”,通信虽然高效了,但是仍需处理资源竞争问题(因为这些资源线程都可以拿,所以竞争力度非常大)。

  • 资源抢占严重: 单进程内的线程(如 JS 线程、GUI 线程、网络线程)共享 CPU 和内存资源,容易出现 “抢资源” 现象。例如,一个标签页的 JS 执行耗时过长(如复杂计算),会阻塞其他标签页的渲染和交互,导致整个浏览器卡顿。
  • 内存泄漏累积: 单进程中,所有页面和组件的内存无法独立释放。即使关闭某个标签页,其残留的内存泄漏(如未清理的事件监听、DOM 引用)会累积在进程中,随着使用时间增长,浏览器内存占用越来越高,最终变得缓慢甚至无响应。
  • 无法利用多核 CPU: 单进程本质上受限于单核心 CPU 的处理能力,无法通过多核心并行计算提升性能。例如,同时渲染多个复杂页面时,只能依赖单个 CPU 核心依次处理,效率极低。

4. 扩展性受限:功能扩展风险高

浏览器广受欢迎的原因之一是它可以接受各种各样的扩展或是插件,以此来打造专属自己的 Web 体验,但是单进程架构使其扩展性受限。这一点也与 “进程中的线程共享进程所拥有的全部资源” 以及 “进程中的任一线程出错,都会导致整个进程崩溃” 的特性有关。

  • 插件/扩展兼容性差: 单进程中,不同插件、扩展的代码运行在同一环境中,容易出现冲突(如同名变量、API 调用冲突)。例如,两个插件同时修改window对象的属性,会导致彼此功能异常。由于缺乏环境隔离,即便遵循一定的编程规范,也难以完全避免这种因共享环境导致的兼容性问题。
  • 扩展风险传导: 恶意或不稳定的扩展会直接影响整个浏览器。例如,一个扩展陷入死循环,会导致进程内所有功能(包括地址栏输入、书签管理)无法正常工作。

小总结

单进程架构的核心问题在于 “缺乏隔离” —— 资源共享、权限不隔离、故障不隔离,这使得浏览器在稳定性、安全性和性能上都存在严重缺陷。而现代浏览器的多进程架构,通过将不同功能模块拆分到独立进程,从根本上解决了这些问题,为用户提供了更稳定、安全、流畅的浏览体验。
接下来我将为你介绍现代浏览器的多进程架构。

(二)现代浏览器多进程架构

浏览器的进程可以是包含许多不同线程的一个进程,也可以是包含少量通过 IPC 通信的线程的许多不同进程。 浏览器架构实现细节.png(原图出自谷歌开发者文档
现代的浏览器一般是一个多进程应用,为了保证其稳定性、安全性和高效性,浏览器会开启多个进程来处理不同的任务。

多进程架构图

上图只是这些不同的架构是实现细节关于如何构建网络浏览器,没有任何标准规范。一款浏览器的方法可能与另一款浏览器完全不同。
我将以 Chrome浏览器 作为讲解的模板,目前的 Chrome 多进程架构应该如图所示:

Chrome浏览器多进程图.png(原图出自谷歌开发者文档
可以从 Chrome 的任务管理器中验证:

浏览器任务管理器多进程图.jpg

各进程详情

浏览器主进程(Browser Process)

Browser Process 控制应用的“Chrome”部分,包括地址栏、书签、返回和前进按钮。还处理网络浏览器的不可见特权部分,例如网络请求和文件访问。它是浏览器的“大管家”,统筹全局:

  • 界面与交互管理:控制浏览器的 UI 显示(地址栏、书签栏、窗口标题等),响应用户点击、输入网址等操作。
  • 进程调度:管理其他进程(如渲染进程、插件进程等)的创建、销毁与通信,决定何时为新标签页开渲染进程。
  • 资源统筹:处理网络请求(可转发给网络服务进程细化处理)、存储管理(如 Cookie、本地存储的全局管控 ),还负责下载、书签等功能的底层逻辑。
实用工具进程(Utility Process)

Utility Process 是是灵活的 “辅助小能手”,处理通用、零散但必要的任务:

  • 兜底与补充:承担不适合核心进程的杂项工作,比如临时数据清理(缓存文件定期删)、轻量系统交互(获取系统时间 / 基础硬件信息 )。
  • 功能解耦:当某些功能需要独立运行又不想影响主进程时,由它接手,比如简单的日志分析、扩展程序的辅助逻辑(部分扩展后台任务)。
  • 服务容器: 具体来说,Chrome 在向 SOA(SOA 的介绍在本文第三章节) 演进的过程中,将原本耦合在主进程(Browser Process)中的功能拆分为独立的 “服务”(Service),这些服务需要运行在进程中。而Utility Process 就是专门用来承载这些独立服务的进程—— 它不直接处理用户界面或渲染逻辑,而是作为 “工具容器”,为网络、存储、音频解码等通用功能提供运行环境。
    • Network Service(负责网络请求、缓存等)和 Storage Service(负责本地存储、Cookie 管理等)作为 Chrome 核心服务,它们本身是独立的服务,但是其运行载体(进程)是动态决定的。
    • 它们可以运行在独立的进程中,也可能在资源受限时合并到Browser ProcessUtility Process(见“节省更多内存”章节的动图说明)。
    • 简单说:Utility Process“服务容器”,而 Network ServiceStorage Service 是它承载的典型服务。
GPU 进程(GPU Process)

GPU Process 专注图形渲染加速,是浏览器的 “视觉引擎”:

  • 图形计算:接收渲染进程的图形绘制指令(如 3D 动画、Canvas 绘图 ),调用 GPU 硬件加速渲染,减轻 CPU 负担。
  • 跨进程协作:为多个渲染进程提供图形服务(比如多个标签页的视频、动画渲染 ),统一管理 GPU 资源,避免重复占用。
渲染进程(Renderer Process)

Renderer Process 是 “页面渲染工厂”,每个标签页(或同站点的一组标签页)对应一个(或共享)渲染进程多个页面就有多个渲染进程,负责以下工作:

  • 解析与渲染:把 HTML、CSS、JS 转换成可视化页面,构建 DOM 树、CSSOM 树,计算布局、绘制像素。
  • JS 执行:运行页面的 JavaScript 代码,处理交互逻辑(点击事件、网络请求等 ),但会受浏览器 “单线程 JS 引擎” 限制(避免阻塞渲染)。
  • 沙盒隔离:每个渲染进程相互隔离,一个标签页崩溃 / 报错,不影响其他标签页,保障浏览器稳定性。
插件进程(Plugin Process)

Plugin Process 是为浏览器插件(如旧版 Flash、特定 PDF 阅读器 )单独开的 “隔离容器”,当页面有插件运行的时候,它才会进行工作,负责以下任务:

  • 安全防护:插件往往功能封闭、代码复杂,单独进程运行可防止插件崩溃 / 漏洞影响浏览器主程序。
  • 功能承载:处理插件专属逻辑(如 Flash 动画渲染、特殊格式文件解析 ),与渲染进程协同(但又独立),让页面能调用插件能力。

节省更多内存 - Chrome 中的服务化

一般来说,当 Chrome 在强大的硬件上运行时,它可能会将每项服务拆分为不同的进程,以提高稳定性,但如果它在资源受限的设备上运行,Chrome 会将服务合并到一个进程中,以节省内存占用空间。在此变更之前,Android 等平台就已采用类似的方法来合并进程以减少内存用量。

节省内存 - 浏览器进程的服务化.gif(原图出自谷歌开发者文档

渲染进程中的线程

在这些进程里面,和用户体验有最直接关联的就是渲染进程。它直接决定了用户看到的网页呈现效果和交互体验,我将为大家重点介绍渲染进程里面的线程。

渲染进程之线程图.jpg

1. GUI 渲染线程
  • 负责解析 HTML 和 CSS,构建 DOM 树、CSSOM 树和渲染树。
  • 进行页面的布局(Layout)和绘制(Paint)。
  • 当页面的样式或结构发生变化时,会重新执行布局和绘制操作。
2. JavaScript 引擎线程
  • 负责执行 JavaScript 代码。
  • JavaScript 引擎是单线程的,这意味着同一时间只能执行一段 JavaScript 代码。
  • 它与 GUI 渲染线程是互斥的,当 JavaScript 引擎线程执行时,GUI 渲染线程会被挂起,反之亦然。这也是为什么长时间运行的 JavaScript 代码会导致页面卡顿的原因。
3. 事件触发线程
  • 负责管理浏览器的事件队列,当有事件触发时(如点击事件、加载完成事件等),会将事件放入队列中,等待 JavaScript 引擎线程处理。
  • 它会不断地检查队列中是否有需要处理的事件,一旦有,就会将其交给 JavaScript 引擎线程执行。
4. 定时器线程
  • 负责处理定时器相关的任务,如 setTimeout 和 setInterval。
  • 当定时器的时间到达时,会将对应的回调函数放入事件队列中,等待 JavaScript 引擎线程执行。
5. 异步 HTTP 请求线程
  • 当 JavaScript 发起异步 HTTP 请求时,该线程会负责处理请求的发送和响应的接收。
  • 当请求完成后,会将响应结果和回调函数放入事件队列中,等待 JavaScript 引擎线程处理。

(三)多进程架构的缺陷

尽管浏览器的多进程架构在稳定性、安全性等方面有诸多优势,但也存在一些不可忽视的缺陷。

1. 资源占用较高

每个进程都需要独立的内存空间来存储代码、数据等资源,当用户打开多个标签页时,会产生多个渲染进程以及对应的辅助进程,这会导致内存占用大幅增加。例如,打开十几个标签页后,浏览器的内存占用可能达到数 GB,对于内存资源有限的设备来说,容易出现卡顿、死机等问题。同时,多个进程运行时也会消耗更多的 CPU 资源,在一些低配设备上可能会影响整体系统的运行速度。

2. 进程间通信成本高

多进程架构下,不同进程之间的通信需要通过 IPC(进程间通信)机制来实现。虽然 IPC 机制能够保证进程间的数据交换,但相比同一进程内线程之间的直接内存访问,其通信效率较低。例如,渲染进程需要获取网络资源时,要通过 IPC 向网络进程发送请求,网络进程处理后再通过 IPC 返回结果,这个过程中会产生一定的延迟。对于一些对实时性要求较高的操作,如页面滚动、动画播放等,频繁的进程间通信可能会影响用户体验。

3. 启动速度较慢

浏览器启动时,需要初始化多个进程,包括浏览器主进程、渲染进程、GPU 进程、网络进程等,每个进程的启动都需要完成一系列的初始化操作,如加载代码、分配资源等。这会使得浏览器的启动时间相对较长,尤其是在一些性能较差的设备上,用户可能需要等待几秒甚至十几秒才能正常使用浏览器,影响了用户的初始体验。

4. 内存管理复杂

由于每个进程都有独立的内存空间,当进程结束时,需要及时释放其占用的内存资源。但在实际运行中,可能会出现内存泄漏的情况,即进程已经结束,但部分内存资源没有被正确释放,长期积累会导致可用内存越来越少。此外,不同进程之间的内存无法直接共享,对于一些需要在多个进程间共享的数据,需要进行多次复制,这不仅浪费内存空间,还增加了内存管理的复杂度。

三. 逐步实现的SOA(面向服务)架构

这些问题大家有目共睹,于是 SOA 架构的实现在2016年被Google推上日程。

SOA的起源与发展

SOA(面向服务的架构)的发展是一个从理论探索到实践落地、不断适应技术演进的过程。其起源可追溯至 20 世纪 90 年代,IBM 等企业为解决系统集成难题提出模块化服务理念,2000 年代随着 SOAP 协议和 Web 服务的兴起,SOA 逐步成为企业级系统的主流架构。2010 年后,云计算和微服务的出现推动 SOA 向更细粒度的服务拆分进化,形成 “服务化” 与 “容器化” 结合的新范式。
在浏览器领域,Chrome 从 2016 年开始将传统多进程架构升级为 SOA。

浏览器领域的SOA

  • 核心服务独立化:将网络(Network Service)、存储(Storage Service)等模块从浏览器主进程剥离,形成独立服务;
  • 动态进程管理:根据设备资源情况弹性调整服务部署 —— 高性能设备中服务独立成进程以提升稳定性,资源紧张时合并到 Utility Process 以节省内存;
  • 松耦合通信:服务间通过 IPC 接口交互,既保持功能隔离又能灵活协作,例如渲染进程调用网络服务获取资源时无需关心其具体实现。 SOA 进程模型图.webp(原图来自极客时间《浏览器工作原理与实践》

SOA 带来的价值

  • 架构灵活性:服务可独立升级、替换,如 Chrome 的网络服务重构后,页面加载速度提升 30%;
  • 资源利用率优化:通过进程合并策略,Android 平台上 Chrome 内存占用降低 40%;
  • 开发效率提升:服务复用减少重复开发,例如身份验证服务可被多个标签页共享。

展望

  • 现在过去快十年了,Chrome 的核心服务(如NetworkStorageAudio等)已基本实现服务化,但进程合并策略和部分模块的重构仍在持续优化中
  • 从上面关于实用工具进程(Utility Process) 的介绍,我们可以看出,SOA架构 的开发应该是完成得不错的,让我们继续等待吧,我感觉应该会是一个“王炸”。

结语

终于讲完了浏览器的进程与线程,接下来我就将进军一道经典大厂面试题《输入URL回车后发生了什么?》,想想都有些激动捏,大家等我更新吧💗。
本篇文章有什么错误缺漏,请大家在评论区指正,蟹蟹🙏。