理論篇 | 窺探瀏覽器運行的幕後

這是我參與8月更文挑戰的第3天,活動詳情查看:8月更文挑戰前端

前言

上一篇《開篇 | 關於前端性能優化的探索》中提到,性能優化須要圍繞着網絡請求的過程瀏覽器渲染機制去展開。但其中提到的渲染過程僅僅是關鍵路徑渲染的部分,對於迴流和重繪、UI渲染和JS引擎互斥的緣由還沒有明確。爲了更好的理解這些,接下來深刻了解一下瀏覽器的運做機制。segmentfault

進程和線程

  • 進程是一個程序運行的實例、是線程的容器 操做系統會爲進程分配獨立的內存(進程之間互相獨立);
  • 線程是進程的組成部分,線程共享進程所分配的資源(包括代碼段、數據集、堆等);
  • 一個程序至少有一個進程,一個進程至少有一個線程(一個主線程和若干子線程);
  • 進程是資源分配的最小單位,線程是程序執行的最小單位;
  • 某個線程執行出錯,將會致使整個進程崩潰

windows 任務管理器查看後臺進程:windows

image.png

多進程瀏覽器

本質上來講,瀏覽器也是一個程序,那麼瀏覽器的運行單位就是進程。在 2008 年穀歌發佈 Chrome 多進程瀏覽器以前,市面上幾乎全部瀏覽器都是單進程的,後來由於有着流暢性、安全性、穩定性等較爲明顯的隱患逐漸被優化掉了。目前咱們所使用的瀏覽器都是多進程瀏覽器。api

瀏覽器的進程

瀏覽器的進程主要有如下幾種:瀏覽器

進程類型 做用
主進程(只有一個) 協調、主控。負責如菜單欄、標題欄等界面顯示,文件訪問,前進後退,以及子進程管理等
GPU 進程 GPU(圖形處理單元),用於3D繪製等
插件進程 每一個插件對應一個進程,僅當使用該插件時才建立(沙箱模式)
網絡進程 負責頁面的網絡資源加載,以前屬於瀏覽器主進程中的一個模塊,後來才獨立出來
渲染進程(瀏覽器內核 ) 默認爲每一個標籤窗口頁開闢一個獨立的渲染進程(沙箱模式), 負責將 HTML、CSS 和 JavaScript 等資源轉爲可交互的頁面,其中包含多個子線程,即 JS 引擎線程、GUI 渲染線程、事件觸發線程、定時觸發器線程、異步 HTTP 請求線程等

打開 Chrome 任務管理器(快捷鍵 Shift+Esc )能夠查看瀏覽器當前的進程安全

  image.png

瀏覽器多進程的優點

  • 進程間所分配的運行資源相對獨立,即使某個進程意外崩潰,也不至於對整個瀏覽器形成影響
  • 多進程充分利用多核優點
  • 方便使用沙盒模型隔離插件等進程,提升瀏覽器穩定性

渲染進程是多線程的

渲染進程是與頁面渲染和性能優化最密切相關的部分。這個進程是多線程的,它主要有GUI渲染線 程、JS引擎線程、事件觸發線程、定時器觸發線程以及異步HTTP請求線程。性能優化

下面重點認識一下這些線程:markdown

GUI渲染線程

  • 負責渲染瀏覽器界面,解析HTML,CSS,構建DOM樹和RenderObject樹,佈局和繪製等。
  • 當界面須要重繪(Repaint)或因爲某種操做引起迴流(reflow)時,該線程就會執行
  • 注意,GUI渲染線程與JS引擎線程是互斥的,當JS引擎執行時GUI線程會被掛起(至關於被凍結了),GUI更新會被保存在一個隊列中等到JS引擎空閒時當即被執行。

JS引擎線程

  • 也稱爲JS內核,負責處理Javascript腳本程序。(例如V8引擎)
  • JS引擎線程負責解析Javascript腳本,運行代碼。
  • JS引擎一直等待着任務隊列中任務的到來,而後加以處理,一個Tab頁(renderer進程)中不管何時都只有一個JS線程在運行JS程序(JS是單線程的)
  • 一樣注意,GUI渲染線程與JS引擎線程是互斥的,因此若是JS執行的時間過長,這樣就會形成頁面的渲染不連貫,致使頁面渲染加載阻塞。

事件觸發線程

  • 用來控制事件循環(能夠理解爲給 JS 引擎線程打輔助)
  • 當JS引擎執行代碼塊如setTimeOut時(或來自瀏覽器內核的其餘線程的任務,如鼠標點擊、AJAX異步請求等),會將對應任務添加到事件線程中
  • 當對應的事件符合觸發條件被觸發時,該線程會把事件添加到待處理隊列的隊尾,等待JS引擎的處理
  • 注意,因爲JS的單線程關係,因此這些待處理隊列中的事件都得排隊等待JS引擎處理(當JS引擎空閒時纔會去執行)

定時觸發器線程

  • 傳說中的setIntervalsetTimeout所在線程
  • 瀏覽器定時計數器並非由JavaScript引擎計數的,(由於JavaScript引擎是單線程的, 若是處於阻塞線程狀態就會影響記計時的準確)
  • 所以經過單獨線程來計時並觸發定時(計時完畢後,添加到事件隊列中,等待JS引擎空閒後執行)
  • 注意,W3C在HTML標準中規定,規定要求setTimeout中低於4ms的時間間隔算爲4ms。

異步http請求線程

  • 在XMLHttpRequest在鏈接後是經過瀏覽器新開一個線程請求
  • 將檢測到狀態變動時,若是設置有回調函數,異步線程就產生狀態變動事件,將這個回調再放入事件隊列中。再由JavaScript引擎執行。

小結

image.png

多線程與JS事件循環

有了以上的認知基礎,我們再結合着線程來回顧一下 JS 的事件循環機制,這裏我按着本身的理解畫了張示意圖:網絡

深刻理解瀏覽器運行機制 —— 事件循環機制.jpg

  • 主線程運行時會產生執行棧
  • 棧中的代碼調用某些api時,會觸發異步HTTP請求線程或定時觸發器線程,它們執行完成後的回調,會交給事件觸發線程管理
  • 事件觸發線程管理着一個事件隊列
  • 執行棧中的代碼執行完畢後,就會讀取事件隊列中的事件去執行
  • 瀏覽器爲了可以使得JS內部task與DOM任務可以有序的執行,完成一個 Task 後、下一個Task開始以前,將對頁面進行從新渲染(即交給GUI渲染進程處理)。這裏也再次說明JS引擎線程和UI線程是互斥的、交替進行的。

多線程與頁面渲染流程

認識了多線程瀏覽器的運做機制後,再來看一下從發送請求到頁面渲染的過程。嗯~ 逐漸串起來了~多線程

  • 瀏覽器主進程(Browser)收到用戶請求
  • 通知網絡進程獲取頁面內容(進行網絡請求獲取頁面資源)
    • DNS解析
    • 創建TCP鏈接
    • 發起HTTP請求
    • HTTP響應,返回數據
  • 數據獲取成功後交給渲染進程獲取成功後交給渲染進程(Render)
  • 渲染進程工做(頁面渲染)
    • 解析HTML\CSS,構建 Render 樹
    • 計算佈局、繪製
    • 若是碰到 JS 腳本,渲染進程掛起,JS引擎進程工做(進程互斥)
    • 若是須要獲取資源,網絡進程幫助獲取資源(進程同步)
    • 若是須要進行3D繪製,GPU進程幫助繪製(進程同步)
    • 若是發生迴流/重繪,渲染進程調整 Render 樹,從新計算、繪製
    • 渲染完成
  • GPU合成圖層,顯示到屏幕

image.png

按着本身的理解畫的圖,不知道表述準不許確,若有紕漏請不吝賜教~

參考連接

從瀏覽器多進程到JS單線程,JS運行機制最全面的一次梳理(很不錯,說得很詳細)
史上最全!圖解瀏覽器的工做原理
聊聊 JavaScript 與瀏覽器的那些事 - 引擎與線程

相關文章
相關標籤/搜索