浏览器篇 | 多进程架构
前言
你有没有想过,为什么 Chrome 浏览器打开几个标签页后,任务管理器里就多了一堆进程?为什么一个标签页崩溃了,其他标签页还能正常使用?为什么浏览器比以前的 IE 安全了那么多?
这些问题的答案,都指向同一个关键词:多进程架构。
在前端面试中,“浏览器的多进程架构”是一个非常有区分度的考点。它不考你写代码的能力,而是考你对浏览器底层运行机制的理解深度。能把这个话题讲清楚的人,在面试官眼里至少是”认真研究过浏览器原理”的。
本章我们就来系统拆解 Chrome 的多进程架构:有哪些进程、每个进程负责什么、渲染进程内部有哪些线程、站点隔离是怎么回事、以及为什么选择多进程而不是多线程。
诊断自测
Q1:Chrome 浏览器至少有哪几类进程?各负责什么?
点击查看答案
Chrome 至少有以下几类进程:
- Browser Process(浏览器主进程):管理地址栏、书签、前进/后退,负责网络请求、文件访问等特权操作
- Renderer Process(渲染进程):负责解析 HTML/CSS/JS,渲染页面内容。每个标签页(或每个站点)通常有独立的渲染进程
- GPU Process(GPU 进程):负责 GPU 相关任务,如页面合成、CSS 动画、WebGL
- Plugin Process(插件进程):运行浏览器插件(如 Flash,已淘汰)
- 还有 Utility Process、Extension Process 等辅助进程
Q2:进程和线程的核心区别是什么?
点击查看答案
进程是操作系统分配资源的基本单位,每个进程有独立的内存空间。进程之间相互隔离,一个进程崩溃不会影响其他进程,但进程间通信(IPC)开销较大。
线程是 CPU 调度的基本单位,同一进程内的线程共享该进程的内存空间。线程间通信效率高(共享内存),但一个线程崩溃可能导致整个进程崩溃。
Q3:渲染进程的主线程负责哪些工作?为什么说主线程是”瓶颈”?
点击查看答案
渲染进程的主线程负责:解析 HTML/CSS、执行 JavaScript、计算样式、布局(Layout)、绘制(Paint)。几乎所有核心工作都在主线程上串行执行。当 JS 执行时间过长时,会阻塞布局和绘制,导致页面卡顿,所以主线程是渲染性能的”瓶颈”。
一、先搞清楚:进程 vs 线程
在聊多进程架构之前,先明确两个基础概念。
进程(Process)
进程是操作系统分配资源的最小单位。 每个进程有自己独立的内存空间、代码、数据。进程之间是隔离的——一个进程不能直接读写另一个进程的内存。
你可以把进程想象成一栋独立的房子:每栋房子有自己的水电气(资源),互不干扰。一栋房子着火了(崩溃),不会影响隔壁的房子。
线程(Thread)
线程是 CPU 调度的最小单位。 一个进程可以包含多个线程,它们共享进程的内存空间和资源。
线程就像同一栋房子里的多个房间:它们共用水电气(共享内存),沟通方便(不需要 IPC),但如果其中一个房间的电线短路引发火灾(线程崩溃),整栋房子都可能被烧毁(进程崩溃)。
对比表
| 特性 | 进程 | 线程 |
|---|---|---|
| 内存空间 | 独立 | 共享所在进程的内存 |
| 通信方式 | IPC(管道、共享内存、消息等) | 直接访问共享变量 |
| 创建/销毁开销 | 大 | 小 |
| 一个崩溃的影响 | 不影响其他进程 | 可能导致整个进程崩溃 |
| 安全性 | 高(隔离) | 低(共享内存可能导致竞争) |
二、Chrome 的多进程架构
早期的浏览器(比如 IE6-8)是单进程的:所有标签页、插件、浏览器 UI 都跑在一个进程里。结果就是——一个标签页挂了,整个浏览器一起挂;一个恶意脚本能影响所有打开的页面;一个插件崩溃就带走整个浏览器。
Chrome 从 2008 年诞生之初就采用了多进程架构,彻底解决了这些问题。
四大核心进程
1. Browser Process(浏览器主进程)
只有一个,是整个浏览器的”管理者”。职责包括:
- UI 管理:地址栏、书签栏、前进/后退按钮、标签栏
- 网络请求:发起和管理 HTTP 请求(通过 Network Thread)
- 文件访问:读写磁盘上的文件(下载、缓存等)
- 进程管理:创建和销毁其他进程(渲染进程、GPU 进程等)
- 权限管理:决定哪个渲染进程可以访问哪些资源
Browser Process 拥有操作系统层面的特权,而渲染进程运行在沙箱中,没有这些权限。这是安全架构的核心。
2. Renderer Process(渲染进程)
最核心的进程,负责页面内容的呈现。职责包括:
- 解析 HTML → DOM 树
- 解析 CSS → CSSOM 树
- 执行 JavaScript(通过 V8 引擎)
- 计算布局(Layout)
- 绘制(Paint)
- 合成(部分工作交给合成线程)
每个标签页通常有自己独立的渲染进程(站点隔离策略下更精确的说法是”每个站点一个渲染进程”,后面会详细讲)。这就是为什么一个标签页卡死了,其他标签页不受影响。
渲染进程运行在**沙箱(Sandbox)**中,不能直接访问操作系统资源(文件、网络等)。所有特权操作必须通过 IPC 请求 Browser Process 代为执行。这极大地提升了安全性——即使渲染进程被恶意代码攻破,攻击者也很难突破沙箱。
3. GPU Process(GPU 进程)
负责所有 GPU 相关的任务:
- 页面的合成(Compositing)
- CSS 动画和 transform 的 GPU 加速
- WebGL 渲染
- 视频解码
GPU 进程只有一个,为所有渲染进程提供服务。为什么 GPU 需要独立进程?因为 GPU 操作涉及硬件访问,把它隔离出来可以避免 GPU 驱动的 bug 导致整个浏览器崩溃。
4. Plugin Process(插件进程)
运行浏览器插件(如曾经的 Flash Player)。每个插件一个进程。
随着 Flash 的退场和 Chrome 扩展转向 Web 技术,传统意义上的 Plugin Process 已经很少见了。但 Chrome 扩展(Extension)仍然有自己的进程。
其他进程
- Utility Process:处理一些零散的任务,比如解码图片、解压文件
- Network Service:在新版 Chrome 中,网络请求被抽成了独立的服务进程
- Storage Service:管理存储(IndexedDB、Cache API 等)
- Audio Service:处理音频
架构示意
┌────────────────────────────────────────────┐
│ Browser Process │
│ ┌──────┐ ┌──────────┐ ┌───────────────┐ │
│ │ UI │ │ Network │ │ Storage/File │ │
│ │Thread│ │ Thread │ │ Thread │ │
│ └──────┘ └──────────┘ └───────────────┘ │
└──────────────────┬─────────────────────────┘
│ IPC
┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────────┐
│Renderer│ │Renderer│ │ GPU Process │
│Process │ │Process │ │ │
│(Tab A) │ │(Tab B) │ └────────────┘
└────────┘ └────────┘
三、渲染进程内部的线程
虽然 Chrome 使用多进程架构,但在每个渲染进程内部,又有多个线程协同工作。这部分是理解浏览器渲染性能的关键。
3.1 主线程(Main Thread)
渲染进程中最重要、也是最”忙”的线程。它负责:
- 解析 HTML:构建 DOM 树
- 解析 CSS:构建 CSSOM 树
- 执行 JavaScript:V8 引擎跑在主线程上
- 计算样式:将 CSS 规则应用到 DOM 节点
- 布局(Layout):计算每个元素的位置和大小
- 绘制(Paint):生成绘制指令列表
注意:主线程是单线程的,所有这些工作都是串行执行的。这意味着如果 JS 执行时间太长,后面的布局和绘制就会被阻塞,页面就会卡顿。
主线程的工作流:
JS 执行 → 样式计算 → Layout → Paint → (提交给合成线程)
↑ │
└────────── 一帧(约 16.7ms @60fps)──────┘
如果你的 JS 执行花了 50ms,那这一帧就会超时,用户就会感到卡顿。这就是为什么我们要避免”长任务(Long Task)“——超过 50ms 的任务就算长任务了。
3.2 合成线程(Compositor Thread)
合成线程负责将主线程生成的绘制指令合成最终的页面画面。
关键特性:合成线程独立于主线程运行。 这意味着即使主线程被 JS 阻塞了,合成线程仍然可以处理一些工作,比如:
- 滚动:页面滚动由合成线程直接处理(如果不涉及 JS 事件监听),所以即使主线程很忙,页面也能流畅滚动
- CSS 动画:
transform和opacity的动画可以完全在合成线程中处理,不需要主线程参与
这也解释了为什么用 transform 做动画比用 left/top 更流畅——后者需要主线程计算布局,前者只需要合成线程重新合成。
3.3 光栅化线程(Raster Thread)
合成线程将页面分成多个图块(Tiles),然后交给光栅化线程将每个图块转换成位图(像素数据)。
Chrome 通常有多个光栅化线程并行工作,加速页面的光栅化过程。光栅化的结果会被存储在 GPU 内存中。
3.4 Worker 线程
除了上述浏览器内部的线程,开发者还可以通过 Web API 创建自己的线程:
- Web Worker:可以在后台线程中运行 JS,不阻塞主线程
// main.js
const worker = new Worker('heavy-task.js');
worker.postMessage({ data: largeArray });
worker.onmessage = (e) => {
console.log('计算结果:', e.data);
};
// heavy-task.js
self.onmessage = (e) => {
const result = heavyComputation(e.data);
self.postMessage(result);
};
- Service Worker:运行在独立线程中,充当网页和网络之间的代理,可以拦截请求、管理缓存
- Shared Worker:可以被多个页面共享的 Worker
Worker 线程和主线程之间通过 postMessage 通信(本质上也是消息传递,类似 IPC),不能直接访问 DOM。
线程协作流程
一次完整的页面渲染,各线程的协作流程:
主线程 合成线程 光栅线程 GPU 进程
│ │ │ │
│──── 样式计算 ────► │ │ │
│──── Layout ────► │ │ │
│──── Paint ────► │ │ │
│ (生成绘制指令) │ │ │
│ │ │ │
│── commit ──────────► │ │ │
│ 分割为 Tiles │ │
│ │── rasterize ──► │ │
│ │ 生成位图 │
│ │◄── 位图完成 ─── │ │
│ 合成帧 (draw quads) │ │
│ │── display ─────────────────────► │
│ │ │ 显示到屏幕
四、站点隔离(Site Isolation)
什么是站点隔离
早期 Chrome 的策略是”一个标签页一个渲染进程”。但在 2018 年,Chrome 67 引入了站点隔离(Site Isolation),将粒度从”标签页”细化到了”站点(Site)”。
站点隔离意味着:不同站点的页面,即使在同一个标签页内(比如通过 iframe 嵌入),也会运行在不同的渲染进程中。
<!-- example.com 的页面 -->
<iframe src="https://ads.thirdparty.com/banner"></iframe>
在站点隔离策略下,example.com 和 ads.thirdparty.com 会运行在不同的渲染进程中,即使它们在同一个标签页内显示。
为什么需要站点隔离
安全。主要是为了防御两类攻击:
-
Spectre 漏洞:2018 年曝出的 CPU 漏洞,允许恶意代码通过侧信道攻击读取同一进程中的内存数据。如果两个不同站点在同一进程中,恶意站点可能通过 Spectre 窃取另一个站点的数据(比如银行页面的敏感信息)。站点隔离确保不同站点在不同进程中,从操作系统层面杜绝了这种攻击。
-
一般的安全隔离:即使没有 Spectre,把不同站点隔离在不同进程中,也能防止一个被攻破的渲染进程影响其他站点的数据。
“站点”的定义
站点(Site)= 协议(scheme)+ 注册域名(eTLD+1)。
https://mail.google.com → 站点是 https://google.com
https://docs.google.com → 站点是 https://google.com(同站点)
https://www.example.com → 站点是 https://example.com
http://www.example.com → 站点是 http://example.com(和 https 不同站点!)
注意:mail.google.com 和 docs.google.com 是同站点的,所以它们可能共享同一个渲染进程。
站点隔离的代价
站点隔离提升了安全性,但也增加了资源消耗:
- 更多进程:每个站点一个进程,意味着更多的内存占用
- 跨进程通信:iframe 和父页面如果在不同进程中,通信需要通过 IPC,增加延迟
- 内存消耗:Chrome 官方数据显示,站点隔离会增加约 10-13% 的内存使用
这也是为什么 Chrome 在移动端(内存更紧张)对站点隔离有更保守的策略。
五、为什么选择多进程而不是多线程?
这是面试中经常出现的一个”追问”。既然多进程比多线程消耗更多资源,为什么 Chrome 还要选择多进程?
1. 稳定性:崩溃隔离
多线程共享内存空间,一个线程的 crash(比如访问了非法内存地址)会导致整个进程崩溃。在多进程架构下,一个渲染进程崩溃只会影响对应的标签页,浏览器可以显示一个”Aw, Snap!”错误页面,其他标签页照常工作。
多线程架构:
Tab A 崩溃 → 整个浏览器崩溃 → Tab B、C、D 全部丢失
多进程架构:
Tab A 崩溃 → 只有 Tab A 显示错误页面 → Tab B、C、D 不受影响
2. 安全性:沙箱隔离
操作系统的安全机制是基于进程的。你可以给一个进程设置权限限制(沙箱),但很难给进程内部的某个线程单独设置权限。
Chrome 的渲染进程运行在沙箱中,没有权限:
- 直接读写文件系统
- 直接发起网络请求
- 直接访问剪贴板
- 直接调用操作系统 API
所有这些操作都必须通过 IPC 向 Browser Process 请求。即使渲染进程被攻破(比如通过 JS 漏洞),攻击者也被困在沙箱里,无法危害系统。
在多线程架构下,所有线程共享同一份权限,一个线程被攻破就意味着整个进程的权限泄露。
3. 资源管理:独立回收
多进程架构下,关闭一个标签页就是结束一个进程。操作系统会完整回收该进程的所有资源——内存、文件句柄、网络连接等。不会有内存泄漏残留。
在多线程架构下,关闭一个标签页只是结束一些线程。由于线程共享内存,要彻底清理干净非常困难,容易出现内存泄漏——这正是早期浏览器越用越慢、越用越占内存的原因之一。
4. 并行利用多核 CPU
虽然多线程也能利用多核 CPU,但多进程的并行粒度更大,更适合浏览器这种”多个独立页面并行运行”的场景。操作系统的进程调度器也能更好地分配 CPU 时间。
多进程的代价
当然,多进程也有代价:
- 内存占用高:每个进程都有自己的 V8 实例、DOM 拷贝等基础设施
- 启动时间长:创建新进程比创建新线程慢
- IPC 开销:进程间通信比线程间通信慢
Chrome 团队也在不断优化这些问题。比如通过”进程合并”策略,在设备资源紧张时,将多个同站点的标签页合并到同一个渲染进程中。
六、从输入 URL 到页面呈现:进程视角
把多进程架构的知识串起来,我们来看看从输入 URL 到看到页面的过程中,各个进程是怎么协作的:
1. 用户输入 URL(Browser Process)
Browser Process 的 UI 线程 收到用户输入,判断是 URL 还是搜索关键词。
2. 发起网络请求(Browser Process)
UI 线程通知 Network 线程发起网络请求。Network 线程处理 DNS 解析、TCP 连接、TLS 握手、发送 HTTP 请求。
3. 读取响应(Browser Process)
Network 线程收到响应后:
- 检查 Content-Type:如果是 HTML,准备交给渲染进程;如果是文件下载,交给下载管理器
- 进行安全检查(SafeBrowsing)
- 进行 CORB(Cross-Origin Read Blocking)检查
4. 创建/选择渲染进程(Browser Process)
Browser Process 为这个页面创建(或选择已有的)渲染进程。
5. 提交导航(Browser Process → Renderer Process)
Browser Process 通过 IPC 向渲染进程发送”提交导航”的消息,同时传递 HTML 数据流。
6. 解析和渲染(Renderer Process)
渲染进程的主线程开始解析 HTML、构建 DOM、解析 CSS、执行 JS、Layout、Paint。
渲染进程的合成线程将 Paint 的结果合成为帧,提交给 GPU Process。
7. 显示(GPU Process)
GPU Process 将合成的帧显示到屏幕上。
用户输入 → Browser Process → Network → 安全检查 → 选择 Renderer
↓ IPC
Renderer Process → 解析/渲染 → 合成
↓ IPC
GPU Process → 显示到屏幕
常见误区
误区一:“一个标签页就是一个进程”
在站点隔离之前,大致是这样。但站点隔离启用后,一个标签页可能有多个进程(主页面一个,每个跨站 iframe 一个),而同一站点的多个标签页在资源紧张时可能共享一个进程。准确说法是”Chrome 使用以站点为粒度的进程分配策略”。
误区二:“JS 是单线程的,所以浏览器也是单线程的”
JS 的执行确实是单线程的(在主线程上),但浏览器本身是多进程多线程的。渲染进程内部就有主线程、合成线程、光栅线程等多个线程。而且你可以通过 Web Worker 创建额外的 JS 执行线程。说”JS 是单线程”没问题,但说”浏览器是单线程”就大错特错了。
误区三:“多进程比多线程一定好”
多进程在安全性和稳定性上确实优于多线程,但代价是更高的内存占用和 IPC 开销。在资源受限的设备(比如低端手机)上,Chrome 会主动减少进程数量,将多个标签页合并到同一个渲染进程中。没有”一定好”的架构,只有针对具体场景的权衡。
误区四:“合成线程可以完全替代主线程做渲染”
合成线程只能处理不需要重新布局和绘制的操作(比如 transform、opacity 动画和滚动)。任何需要改变 DOM、计算样式、重新布局的操作,都必须回到主线程。合成线程是主线程的”助手”,而不是”替代品”。
小结
本章从进程和线程的基本概念出发,系统梳理了 Chrome 的多进程架构。
核心要点
- Chrome 采用多进程架构:Browser Process 管理全局,Renderer Process 负责页面渲染,GPU Process 处理图形,各司其职
- 渲染进程运行在沙箱中:不能直接访问系统资源,所有特权操作通过 IPC 委托 Browser Process
- 渲染进程内部有多个线程:主线程(解析/JS/布局/绘制)、合成线程(合成/滚动/动画)、光栅线程(图块光栅化)
- 主线程是瓶颈:JS 执行、布局、绘制都在主线程串行完成,长任务会导致卡顿
- 站点隔离:不同站点运行在不同进程中,防御 Spectre 等安全威胁
- 选择多进程的原因:崩溃隔离、沙箱安全、资源回收、多核利用
- Web Worker:开发者可以创建额外的 JS 线程,分担主线程压力
本章思维导图
- 基础概念
- 进程:独立内存空间,隔离性好,开销大
- 线程:共享内存,通信快,隔离性差
- Chrome 进程
- Browser Process:UI、网络、文件、进程管理
- Renderer Process:HTML/CSS/JS 解析、布局、绘制
- GPU Process:合成、动画、WebGL
- Plugin Process:插件(已逐渐淘汰)
- 其他:Utility、Network Service 等
- 渲染进程内部线程
- 主线程:解析、JS 执行、样式计算、Layout、Paint
- 合成线程:合成、滚动、transform/opacity 动画
- 光栅线程:图块 → 位图
- Worker 线程:Web Worker、Service Worker
- 站点隔离
- 不同站点不同进程(即使在同一标签页)
- 防御 Spectre 等侧信道攻击
- 站点 = 协议 + eTLD+1
- 代价:更多内存,更多 IPC
- 为什么多进程
- 崩溃隔离
- 沙箱安全
- 资源回收
- 多核利用
- 代价:内存、启动时间、IPC
练习挑战
第一题(⭐ 基础):判断对错
以下说法哪些是正确的?
A. Chrome 的每个标签页一定有独立的渲染进程 B. 渲染进程不能直接发起网络请求 C. Web Worker 运行在主线程上 D. transform 动画可以在合成线程上完成,不需要主线程参与 E. GPU Process 只有一个,服务于所有渲染进程
点击查看答案与解析
- A 错误:不是”一定”。同站点的多个标签页在资源紧张时可能共享渲染进程;一个标签页内如果有跨站 iframe,可能有多个渲染进程
- B 正确:渲染进程在沙箱中,网络请求由 Browser Process 的 Network 线程处理
- C 错误:Web Worker 运行在独立的线程中,这正是它的意义——不阻塞主线程
- D 正确:transform 和 opacity 动画可以完全由合成线程处理
- E 正确:GPU Process 全局只有一个
正确答案:B、D、E
第二题(⭐⭐ 进阶):分析进程分配
假设用户打开了以下页面:
- Tab 1:
https://mail.google.com - Tab 2:
https://docs.google.com - Tab 3:
https://www.example.com,其中嵌入了<iframe src="https://ads.adserver.com/banner">
在启用站点隔离的情况下,至少需要几个渲染进程?分别负责什么?
点击查看答案与解析
至少需要 3 个渲染进程:
- 进程 A:负责
https://mail.google.com和https://docs.google.com。虽然它们是不同的标签页,但它们是同站点的(都属于https://google.com),可以共享渲染进程。 - 进程 B:负责 Tab 3 中
https://www.example.com的主页面。 - 进程 C:负责 Tab 3 中
https://ads.adserver.com的 iframe。虽然 iframe 在 Tab 3 内部显示,但它和example.com不是同站点,所以需要独立的渲染进程。
除此之外,还有 Browser Process、GPU Process 等,但这里只问渲染进程的数量。
第三题(⭐⭐⭐ 综合):架构设计思考
假设你要设计一个浏览器的标签页管理策略。设备内存有限(2GB),用户可能同时打开 20 个标签页。你需要在”性能/安全/资源占用”之间做权衡。请描述你的进程分配策略,包括:
- 什么情况下创建新进程
- 什么情况下复用已有进程
- 如何处理内存压力
点击查看答案与解析
参考策略(类似 Chrome 在低端设备上的做法):
1. 创建新进程的条件:
- 打开不同站点的新标签页时,创建新渲染进程
- 跨站 iframe 尽量隔离(安全优先)
- 用户交互频繁的前台标签页优先获得独立进程
2. 复用已有进程的条件:
- 同站点的标签页共享渲染进程(如同一个域名下的多个页面)
- 当进程总数超过阈值(比如根据内存动态计算,2GB 设备约 10-15 个渲染进程),开始将新的同站点标签页合并到已有进程
- 后台标签页(长时间未交互)可以被”冻结”甚至”丢弃”——保留 URL 等元信息,释放渲染进程
3. 内存压力处理:
- 监控系统可用内存。当内存紧张时:
- 先丢弃后台标签页的渲染进程(用户切回来时重新加载)
- 合并同站点的标签页到同一进程
- 压缩后台标签页的内存(V8 的 Memory Pressure 通知)
- 最后手段:提示用户关闭标签页
这个策略的核心思想是:安全底线不动摇(跨站隔离),性能在资源允许时最大化(独立进程),资源紧张时优雅降级(合并/冻结/丢弃)。
自我检测
- 能说清楚进程和线程的区别,以及它们在安全性、稳定性、通信方式上的差异
- 能列出 Chrome 的四大核心进程及各自的职责
- 能说出渲染进程内部的主要线程(主线程、合成线程、光栅线程)及各自的分工
- 能解释为什么主线程是渲染性能的”瓶颈”
- 能说清楚站点隔离的含义、目的和代价
- 能从稳定性、安全性、资源管理三个角度解释为什么 Chrome 选择多进程而不是多线程
- 能描述从输入 URL 到页面呈现的过程中,各个进程是如何协作的
- 能说出 Web Worker 的作用和它与主线程的通信方式
购买课程解锁全部内容
大厂前端面试通关:71 篇构建完整知识体系
¥89.90