這是我參與8月更文挑戰的第3天,活動詳情查看:8月更文挑戰前端
前言
上一篇《開篇 | 關於前端性能優化的探索》中提到,性能優化須要圍繞着網絡請求的過程和瀏覽器渲染機制去展開。但其中提到的渲染過程僅僅是關鍵路徑渲染的部分,對於迴流和重繪、UI渲染和JS引擎互斥的緣由還沒有明確。爲了更好的理解這些,接下來深刻了解一下瀏覽器的運做機制。segmentfault
進程和線程
- 進程是一個程序運行的實例、是線程的容器 , 操做系統會爲進程分配獨立的內存(進程之間互相獨立);
- 線程是進程的組成部分,線程共享進程所分配的資源(包括代碼段、數據集、堆等);
- 一個程序至少有一個進程,一個進程至少有一個線程(一個主線程和若干子線程);
- 進程是資源分配的最小單位,線程是程序執行的最小單位;
- 某個線程執行出錯,將會致使整個進程崩潰
windows 任務管理器查看後臺進程:windows
![image.png image.png](http://static.javashuo.com/static/loading.gif)
多進程瀏覽器
本質上來講,瀏覽器也是一個程序,那麼瀏覽器的運行單位就是進程。在 2008 年穀歌發佈 Chrome 多進程瀏覽器以前,市面上幾乎全部瀏覽器都是單進程的,後來由於有着流暢性、安全性、穩定性等較爲明顯的隱患逐漸被優化掉了。目前咱們所使用的瀏覽器都是多進程瀏覽器。api
瀏覽器的進程
瀏覽器的進程主要有如下幾種:瀏覽器
進程類型 |
做用 |
主進程(只有一個) |
協調、主控。負責如菜單欄、標題欄等界面顯示,文件訪問,前進後退,以及子進程管理等 |
GPU 進程 |
GPU(圖形處理單元),用於3D繪製等 |
插件進程 |
每一個插件對應一個進程,僅當使用該插件時才建立(沙箱模式) |
網絡進程 |
負責頁面的網絡資源加載,以前屬於瀏覽器主進程中的一個模塊,後來才獨立出來 |
渲染進程(瀏覽器內核 ) |
默認爲每一個標籤窗口頁開闢一個獨立的渲染進程(沙箱模式), 負責將 HTML、CSS 和 JavaScript 等資源轉爲可交互的頁面,其中包含多個子線程,即 JS 引擎線程、GUI 渲染線程、事件觸發線程、定時觸發器線程、異步 HTTP 請求線程等 |
打開 Chrome 任務管理器(快捷鍵 Shift+Esc
)能夠查看瀏覽器當前的進程安全
![image.png image.png](http://static.javashuo.com/static/loading.gif)
瀏覽器多進程的優點
- 進程間所分配的運行資源相對獨立,即使某個進程意外崩潰,也不至於對整個瀏覽器形成影響
- 多進程充分利用多核優點
- 方便使用沙盒模型隔離插件等進程,提升瀏覽器穩定性
渲染進程是多線程的
渲染進程是與頁面渲染和性能優化最密切相關的部分。這個進程是多線程的,它主要有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引擎空閒時纔會去執行)
定時觸發器線程
- 傳說中的
setInterval
與setTimeout
所在線程
- 瀏覽器定時計數器並非由JavaScript引擎計數的,(由於JavaScript引擎是單線程的, 若是處於阻塞線程狀態就會影響記計時的準確)
- 所以經過單獨線程來計時並觸發定時(計時完畢後,添加到事件隊列中,等待JS引擎空閒後執行)
- 注意,W3C在HTML標準中規定,規定要求setTimeout中低於4ms的時間間隔算爲4ms。
異步http請求線程
- 在XMLHttpRequest在鏈接後是經過瀏覽器新開一個線程請求
- 將檢測到狀態變動時,若是設置有回調函數,異步線程就產生狀態變動事件,將這個回調再放入事件隊列中。再由JavaScript引擎執行。
小結
![image.png image.png](http://static.javashuo.com/static/loading.gif)
多線程與JS事件循環
有了以上的認知基礎,我們再結合着線程來回顧一下 JS 的事件循環機制,這裏我按着本身的理解畫了張示意圖:網絡
![深刻理解瀏覽器運行機制 —— 事件循環機制.jpg](http://static.javashuo.com/static/loading.gif)
- 主線程運行時會產生執行棧
- 棧中的代碼調用某些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 image.png](http://static.javashuo.com/static/loading.gif)
按着本身的理解畫的圖,不知道表述準不許確,若有紕漏請不吝賜教~
參考連接
從瀏覽器多進程到JS單線程,JS運行機制最全面的一次梳理(很不錯,說得很詳細)
史上最全!圖解瀏覽器的工做原理
聊聊 JavaScript 與瀏覽器的那些事 - 引擎與線程