浏览器底层原理:打开一个页面,Chrome 到底背着我干了啥?

48 阅读8分钟

前言:

作为前端开发者,我们每天都在和浏览器打交道。但你有没有试过,当你仅仅打开了一个 Chrome 标签页,反手打开“任务管理器”,却发现里面躺着好几个进程?

“我明明只开了一个网页,Chrome 你是不是在偷我内存挖矿?”

别急,今天我们就扒开 Chrome 的外衣,从底层架构的角度,看看它到底是由什么组成的。看完这篇文章,你不仅能理解为什么 Chrome 这么“吃”内存,还能明白为什么它淘汰了当年的 IE 浏览器。

一、进程(Process)与线程(Thread)

在聊浏览器之前,我们得先搞定两个计算机操作系统的核心概念:进程线程。这俩货是面试必考题,也是理解浏览器的基石。

为了通俗易懂,我们把 CPU 想象成一座超大的工厂

1. 进程(Process):工厂里的“车间”

  • 定义:进程是操作系统进行资源分配的最小单位。

  • 特点

    • 当你启动一个程序(比如打开 Chrome),操作系统就会给它开辟一块内存空间,这就好比在工厂里盖了一个车间
    • 独立性:车间与车间之间是隔离的。A 车间着火了(崩溃),通常不会烧到 B 车间(除非火势太大把整个厂烧了)。
    • 私有资源:A 车间里的原材料(内存数据),B 车间是拿不到的。如果非要交换数据,得走专门的管道,这叫 IPC(进程间通信)

76E758A8-DD05-44D3-AE4B-BB6C440CF519.png

这张图片就体现了进程是资源分配最小的单位,在一个线程中,代码数据,文件等资源,经由线程分配给线程使用

2. 线程(Thread):车间里的“打工人”

  • 定义:线程是 CPU 调度和执行的基本单位。

  • 特点

    • 一个车间(进程)里至少得有一个工人(主线程)在干活,当然也可以有很多工人(多线程)一起干活。
    • 资源共享:同一个车间里的工人们,共享车间里的空间和工具(内存)。
    • 风险:如果一个工人发疯把车间的承重柱砸了(线程崩溃),整个车间都会塌下来(进程崩溃),车间里其他人也得跟着完蛋。

AF57C286-F4DC-4F06-982D-DF1E4906ED21.png

这张图片则体现了在一个进程中,线程之间资源共享的概念


二、 忆往昔:单进程浏览器的“黑暗时代”

在很久很久以前(大概是 IE6 称霸的年代),主流浏览器,包括当时的IE浏览器,都是单进程的。
也就是说,浏览器的所有功能——网络请求、页面渲染、JS 执行、插件运行——统统都在一个车间里搞定。

这就导致了三个著名的“劝退”问题:

  1. 不稳定:你打开了一个视频网站,上面的 Flash 插件崩了,或者你的某段 JS 代码写了个死循环。结果呢?不仅仅是这个网页卡死,而是整个浏览器窗口连同你正在写的未保存的博客,瞬间全部消失。
  2. 不流畅:JS 执行和页面绘制都在一个线程里。如果 JS 在疯狂计算,页面渲染就得干等着。给用户的感觉就是:卡!
  3. 不安全:既然都在一个车间,恶意的插件或者脚本可以随意读取内存里的数据,你的银行卡密码可能就在隔壁。

39E54AC2-7429-472A-82A6-D95A301D7FD8.png


三、 Chrome 的革命:多进程架构

Chrome 刚出来的时候,打出的王炸就是——多进程架构。它不搞“大锅饭”,而是搞“精细化分工”。

当你打开 Chrome 时,它不仅仅启动了一个进程,而是启动了一个豪华进程天团

19B8E5FB-6566-4E70-A3DD-0D5A40B3EB8F.png

1. 顶层 BOSS:浏览器主进程 (Browser Process)

  • 地位:它是整个浏览器的“总管”。

  • 职责

    • 负责浏览器界面的显示(地址栏、书签、前进后退按钮)。
    • 负责用户交互(你点了哪里,滚轮滚了多远)。
    • 负责管理其他子进程(比如创建和销毁标签页)。
    • 一句话:它是对外的门面,也是对内的指挥官。

2. 苦力一号:渲染进程 (Render Process)

  • 地位:前端开发者的“主战场”。

  • 职责

    • 把 HTML、CSS、JS 变成用户肉眼能看到的网页。
    • 排版引擎 Blink 和 JS 引擎 V8 都在这儿干活。
    • 默认机制:Chrome 默认会为每个 Tab 标签页创建一个独立的渲染进程(虽然现在有优化策略,比如共用同一站点的进程,但原则上是隔离的)。
    • 沙箱模式 (Sandbox) :为了安全,这个进程被关在一个“沙箱”里,不能随意读写硬盘文件,想搞破坏?没门。

3. 各种专业户:

  • GPU 进程

    • 本来 GPU 是为了处理 3D CSS 的,后来发现它画 UI 也很快。为了不让它把主进程拖崩,Chrome 把它独立了出来。现在的网页加速、Canvas 绘制全靠它。
  • 网络进程 (Network Process)

    • 以前网络请求是在主进程里,后来独立出来专门负责下载资源、处理 Cookie 等。
  • 插件进程 (Plugin Process)

    • 专门负责 Flash 等插件。现在 Flash 已经入土了,这个进程的存在感越来越低,但它的隔离性保证了插件崩溃不会带走浏览器。

✅ 灵魂拷问:打开一个页面,至少需要几个进程?

答案是:4 个(虽然有优化,但这是标准答案)。

  1. 浏览器主进程(管界面的)
  2. 网络进程(去抓网页代码的)
  3. GPU 进程(帮忙画图的)
  4. 渲染进程(执行代码和渲染页面的)

好处显而易见
就算你写的代码把渲染进程搞崩了(Tab 页白屏),用户的浏览器主窗口还在,其他 Tab 页还在,点击“关闭”按钮,生活依然美好。


四、 深入渲染进程:前端人的“单线程”之痛

很多面试官会问:“JS 既然是单线程的,那为什么浏览器能同时处理这么多事?”
其实这里有个误区。浏览器是多进程多线程的,但 JS 的执行是在渲染进程中的一个特定线程上进行的。

让我们走进渲染进程这个“车间”内部,看看里面有哪些核心“工人”(线程):

1. GUI 渲染线程

  • 负责渲染浏览器界面,解析 HTML、CSS,构建 DOM 树和 Render 树,布局和绘制。
  • 注意:它和 JS 引擎线程是互斥的!

2. JS 引擎线程 (V8)

  • 这就是传说中的JS 单线程本尊。
  • 负责解析 Javascript 脚本,运行代码。
  • 互斥原理:因为 JS 可以操作 DOM(比如把一个 div 删了),如果 JS 在操作 DOM 的同时,GUI 线程也在画这个 DOM,那到底听谁的?为了避免混乱,浏览器规定:当 JS 引擎执行时,GUI 线程会被挂起(冻结),直到 JS 任务队列空闲。
  • 后果:如果你的 JS 写了个 10 亿次的循环,JS 线程一直在这个循环里出不来,GUI 线程就一直不能画画,页面看起来就是卡死的。

3. 其他辅助线程

  • 事件触发线程:控制 Event Loop(事件循环)。当 onclick 等事件被触发时,把任务扔进 JS 引擎的队列里排队。
  • 定时器触发线程:setTimeout 和 setInterval 所在的地方。JS 引擎太忙了,为了计时准确,计时是由这个线程单独来计的,时间到了再把回调扔回 JS 队列。
  • 异步 HTTP 请求线程:处理 AJAX 请求的。请求回来了,把回调函数扔进 JS 队列。

五、 总结与思考

看到这里,我们可以重新回答文章开头的问题了:

1.Chrome 打开页面意味着什么?
意味着操作系统给它分配了 PID,启动了主进程,并根据架构拉起了 GPU、网络、渲染等一堆子进程。

2. 为什么要多进程?
为了隔离。A 页面崩了不影响 B 页面;插件崩了不影响主程序;渲染引擎在这个沙箱里乱搞,不会破坏外面的系统文件。

3. JS 既然是单线程,为什么还能异步?
因为浏览器是多线程的!JS 引擎自己是单线程傻干活,但浏览器给了它很多帮手(定时器线程、网络线程)。JS 只要发个号令“你去下载图片”,然后继续干别的,等图片下载完了,网络线程会把结果送到 JS 嘴边。

浏览器进化的启示

从单进程到多进程,本质上是用空间换时间,用资源换稳定
Chrome 虽然是“内存杀手”,但它给了用户极致的流畅体验和极高的稳定性。作为开发者,我们理解了这些底层原理,就能更好地优化代码:

  • 避免长时间占用 JS 主线程(防止页面卡顿)。
  • 合理使用异步编程(利用多线程优势)。
  • 理解 V8 的工作环境,写出更高效的代码。

0be86da023b29b3f16cc17935163a62b.jpg