摘要:引言一直對瀏覽器的進程線程的運行一無所知,經過一次的刷刷刷相關的博客之后,對其有了初步的了解,是時候該總結一波了。瀏覽器內的進程知道了進程與線程之間的關系之后,下面是瀏覽器與進程的關系了。
引言
一直對瀏覽器的進程、線程的運行一無所知,經過一次的刷刷刷相關的博客之后,對其有了初步的了解,是時候該總結一波了。
進程、線程之間的關系一個進程有一個或多個線程,線程之間共同完成進程分配下來的任務。打個比方:
假如進程是一個工廠,工廠有它的獨立的資源
工廠之間相互獨立
線程是工廠中的工人,多個工人協作完成任務
工廠內有一個或多個工人
工人之間共享空間
再完善完善概念:
工廠的資源 -> 系統分配的內存(獨立的一塊內存)
工廠之間的相互獨立 -> 進程之間相互獨立
多個工人協作完成任務 -> 多個線程在進程中協作完成任務
工廠內有一個或多個工人 -> 一個進程由一個或多個線程組成
工人之間共享空間 -> 同一進程下的各個線程之間共享程序的內存空間(包括代碼段、數據集、堆等)
進程是cpu資源分配的最小單位(是能擁有資源和獨立運行的最小單位),線程是cpu調度的最小單位(線程是建立在進程的基礎上的一次程序運行單位)。
瀏覽器內的進程知道了進程與線程之間的關系之后,下面是瀏覽器與進程的關系了。首先,瀏覽器是多進程的,之所以瀏覽器能夠運行,是因為系統給瀏覽器分配了資源,如cpu、內存,簡單的說就是,瀏覽器每打開一個標簽頁,就相當于創建了一個獨立的瀏覽器進程。例如我們查看chrome里面的任務管理器。
注意: 在這里瀏覽器應該也有自己的優化機制,有時候打開多個tab頁后,可以在Chrome任務管理器中看到,有些進程被合并了(譬如打開多個空白標簽頁后,會發現多個空白標簽頁被合并成了一個進程),所以每一個Tab標簽對應一個進程并不一定是絕對的。
除了瀏覽器的標簽頁進程之外,瀏覽器還有一些其他進程來輔助支撐標簽頁的進程,如下:
① Browser進程:瀏覽器的主進程(負責協調、主控),只有一個。作用有
負責瀏覽器界面顯示,與用戶交互。如前進,后退等
負責各個頁面的管理,創建和銷毀其他進程
網絡資源的管理,下載等
② 第三方插件進程:每種類型的插件對應一個進程,僅當使用該插件時才創建
③ GPU進程:最多一個,用于3D繪制等
④ 瀏覽器渲染進程(瀏覽器內核),Renderer進程,內部是多線程的,也就是我們每個標簽頁所擁有的進程,互不影響,負責頁面渲染,腳本執行,事件處理等
如下圖:
瀏覽器內核瀏覽器內核,即我們的渲染進程,有名Renderer進程,我們頁面的渲染,js的執行,事件的循環都在這一進程內進行,也就是說,該進程下面擁有著多個線程,靠著這些現成共同完成渲染任務。那么這些線程是什么呢,如下:
① 圖形用戶界面GUI渲染線程
負責渲染瀏覽器界面,包括解析HTML、CSS、構建DOM樹、Render樹、布局與繪制等
當界面需要重繪(Repaint)或由于某種操作引發回流(reflow)時,該線程就會執行
② JS引擎線程
JS內核,也稱JS引擎,負責處理執行javascript腳本
等待任務隊列的任務的到來,然后加以處理,瀏覽器無論什么時候都只有一個JS引擎在運行JS程序
③ 事件觸發線程
聽起來像JS的執行,但是其實歸屬于瀏覽器,而不是JS引擎,用來控制時間循環(可以理解,JS引擎自己都忙不過來,需要瀏覽器另開線程協助)
當JS引擎執行代碼塊如setTimeout時(也可來自瀏覽器內核的其他線程,如鼠標點擊、AJAX異步請求等),會將對應任務添加到事件線程中
當對應的事件符合觸發條件被觸發時,該線程會把事件添加到待處理隊列的隊尾,等待JS引擎的處理
注意:由于JS的單線程關系,所以這些待處理隊列中的事件都得排隊等待JS引擎處理(當JS引擎空閑時才會去執行)
④ 定時觸發器線程
setInterval與setTimeout所在線程
定時計時器并不是由JS引擎計時的,因為如果JS引擎是單線程的,如果JS引擎處于堵塞狀態,那會影響到計時的準確
當計時完成被觸發,事件會被添加到事件隊列,等待JS引擎空閑了執行
注意:W3C的HTML標準中規定,setTimeout中低與4ms的時間間隔算為4ms
⑤ 異步HTTP請求線程
在XMLHttpRequest在連接后新啟動的一個線程
線程如果檢測到請求的狀態變更,如果設置有回調函數,該線程會把回調函數添加到事件隊列,同理,等待JS引擎空閑了執行
瀏覽器內核,放圖加強記憶:
為什么JS引擎是單線程的JavaScript作為一門客戶端的腳本語言,主要的任務是處理用戶的交互,而用戶的交互無非就是響應DOM的增刪改,使用事件隊列的形式,一次事件循環只處理一個事件響應,使得腳本執行相對連續。如果JS引擎被設計為多線程的,那么DOM之間必然會存在資源競爭,那么語言的實現會變得非常臃腫,在客戶端跑起來,資源的消耗和性能將會是不太樂觀的,故設計為單線程的形式,并附加一些其他的線程來實現異步的形式,這樣運行成本相對于使用JS多線程來說降低了很多。
瀏覽器內核中線程之間的關系 GUI渲染線程與JS引擎線程互斥因為JS引擎可以修改DOM樹,那么如果JS引擎在執行修改了DOM結構的同時,GUI線程也在渲染頁面,那么這樣就會導致渲染線程獲取的DOM的元素信息可能與JS引擎操作DOM后的結果不一致。為了防止這種現象,GUI線程與JS線程需要設計為互斥關系,當JS引擎執行的時候,GUI線程需要被凍結,但是GUI的渲染會被保存在一個隊列當中,等待JS引擎空閑的時候執行渲染。
由此也可以推出,如果JS引擎正在進行CPU密集型計算,那么JS引擎將會阻塞,長時間不空閑,導致渲染進程一直不能執行渲染,頁面就會看起來卡頓卡頓的,渲染不連貫,所以,要盡量避免JS執行時間過長。
事件觸發線程、定時觸發器線程、異步HTTP請求線程三個線程有一個共同點,那就是使用回調函數的形式,當滿足了特定的條件,這些回調函數會被執行。這些回調函數被瀏覽器內核理解成事件,在瀏覽器內核中擁有一個事件隊列,這三個線程當滿足了內部特定的條件,會將這些回調函數添加到事件隊列中,等待JS引擎空閑執行。例如異步HTTP請求線程,線程如果檢測到請求的狀態變更,如果設置有回調函數,回調函數會被添加事件隊列中,等待JS引擎空閑了執行。
但是,JS引擎對事件隊列(宏任務)與JS引擎內的任務(微任務)執行存在著先后循序,當每執行完一個事件隊列的時間,JS引擎會檢測內部是否有未執行的任務,如果有,將會優先執行(微任務)。
因為JS引擎是單線程的,當JS執行時間過長會頁面阻塞,那么JS就真的對CPU密集型計算無能為力么?
所以,后來HTML5中支持了 Web Worker。
來自MDN的官方解釋
Web Workers 使得一個Web應用程序可以在與主執行線程分離的后臺線程中運行一個腳本操作。這樣做的好處是可以在一個多帶帶的線程中執行費時的處理任務,從而允許主(通常是UI)線程運行而不被阻塞/放慢。
注意點:
WebWorker可以想瀏覽器申請一個子線程,該子線程服務于主線程,完全受主線程控制。
JS引擎線程與worker線程間通過特定的方式通信(postMessage API,需要通過序列化對象來與線程交互特定的數據)
所以,如果需要進行一些高耗時的計算時,可以多帶帶開啟一個WebWorker線程,這樣不管這個WebWorker子線程怎么密集計算、怎么阻塞,都不會影響JS引擎主線程,只需要等計算結束,將結果通過postMessage傳輸給主線程就可以了。
另外,還有個東西叫 SharedWorker,與WebWorker在概念上所不同。
WebWorker 只屬于某一個頁面,不會和其他標簽頁的Renderer進程共享,WebWorker是屬于Renderer進程創建的進程。
SharedWorker 是由瀏覽器多帶帶創建的進程來運行的JS程序,它被所有的Renderer進程所共享,在瀏覽器中,最多只能存在一個SharedWorker進程。
SharedWorker由進程管理,WebWorker是某一個Renderer進程下的線程。
瀏覽器的渲染流程每個瀏覽器內核的渲染流程不一樣,下面我們主要以webkit為主。
首先是渲染的前奏:
瀏覽器輸入url,瀏覽器主進程接管,開了一個下載線程
然后進行HTTP請求(DNS查詢、IP尋址等等),等待響應,開始下載響應報文。
將下載完的內容轉交給Renderer進程管理
開始渲染...
在說渲染之前,需要理解一些概念:
DOM Tree: 瀏覽器將HTML解析成樹形的數據結構。
CSS Rule Tree:瀏覽器將CSS解析成樹形的數據結構。
Render Tree:DOM樹和CSS規則樹合并后生產Render樹。
layout:有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關系,從而去計算出每個節點在屏幕中的位置。
painting: 按照算出來的規則,通過顯卡,把內容畫到屏幕上。
reflow(回流):當瀏覽器發現某個部分發生了點變化影響了布局,需要倒回去重新渲染,內行稱這個回退的過程叫 reflow。reflow 會從 這個 root frame 開始遞歸往下,依次計算所有的結點幾何尺寸和位置。reflow 幾乎是無法避免的。現在界面上流行的一些效果,比如樹狀目錄的折疊、展開(實質上是元素的顯 示與隱藏)等,都將引起瀏覽器的 reflow。鼠標滑過、點擊……只要這些行為引起了頁面上某些元素的占位面積、定位方式、邊距等屬性的變化,都會引起它內部、周圍甚至整個頁面的重新渲 染。通常我們都無法預估瀏覽器到底會 reflow 哪一部分的代碼,它們都彼此相互影響著。
repaint(重繪):改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部布局的屬性時,屏幕的一部分要重畫,但是元素的幾何尺寸沒有變。
注意:display:none的節點不會被加入Render Tree,而visibility: hidden則會,所以display:none會觸發reflow,visibility: hidden會觸發repaint。
瀏覽器內核拿到響應報文之后,渲染大概分為以下步驟
解析html生產DOM樹。
解析CSS規則。
根據DOM Tree和CSS Tree生成Render Tree。
根據Render樹進行layout,負責各個元素節點的尺寸、位置計算。
繪制Render樹(painting),繪制頁面像素信息。
瀏覽器會將各層的信息發送給GPU,GPU會將各層合成(composite),顯示在屏幕上。
詳細步驟略去,大概步驟如下,渲染完畢后JS引擎開始執行load事件,繪制流程見下圖。
由圖中可以看出,css在加載過程中不會影響到DOM樹的生成,但是會影響到Render樹的生成,進而影響到layout,所以一般來說,style的link標簽需要盡量放在head里面,因為在解析DOM樹的時候是自上而下的,而css樣式又是通過異步加載的,這樣的話,解析DOM樹下的body節點和加載css樣式能盡可能的并行,加快Render樹的生成的速度,當然,如果css是通過js動態添加進來的,會引起頁面的重繪或重新布局。
從有html標準以來到目前為止(2017年5月),標準一直是規定style元素不應出現在body元素中。
前面提到了load事件,那么與DOMContentLoaded事件有什么分別。
當 DOMContentLoaded 事件觸發時,僅當DOM加載完成,不包括樣式表,圖片。 (譬如如果有async加載的腳本就不一定完成)
當 onLoad 事件觸發時,頁面上所有的DOM,樣式表,腳本,圖片都已經加載完成了。 (渲染完畢了)
順序是:DOMContentLoaded -> load
最后寫到這里,總結了也有不少的內容,也對瀏覽器多線程、JS引擎有所了解,后面打算在看看JS的運行機制。前端知識也是無窮無盡,數不清的概念與無數個易忘的知識、各種框架原理,學來學去,還是發現自己知道得太少了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107120.html
摘要:以多線程的形式,允許單個任務分成不同的部分進行運行。提供協調機制,一方面防止進程之間和線程之間產生沖突,另一方面允許進程之間和線程之間共享資源。主線程會不斷的重復上訴過程。 眾所周知,js是單線程的,說到線程,我們首先來仔細辨析一下線程和進程的知識。 一、進程與線程 阮一峰老師的一篇文章寫的很好 cpu會給當前進程分配資源,進程是資源分配的最小單位,進程的資源會分配給線程使用,線程是C...
摘要:事實上,瀏覽器內部的運行機制是,先將通信內容串行化,然后把串行化后的字符串發給子線程,后者再將它還原。當一個的文檔列表中的任何一個對象都是處于完全活動狀態的時候,這個會被稱之為需要激活線程。 瀏覽器中的Web Worker 背景介紹 我們都知道JavaScript這個語言在執行的時候是采用單線程進行執行的,也就是說在同一時間只能做一件事,這也和這門語言有很大的關系,采用同步執行的方式進...
摘要:進程可創建多個線程來執行同一程序的不同部分。就緒等待線程調度。運行線程正常運行阻塞暫停運行,解除阻塞后進入狀態重新等待調度。消亡線程方法執行完畢返回或者異常終止。多線程多的情況下,依次執行各線程的方法,前頭一個結束了才能執行后面一個。 淺談Python多線程 作者簡介: 姓名:黃志成(小黃)博客: 博客 線程 一.什么是線程? 操作系統原理相關的書,基本都會提到一句很經典的話: 進程...
閱讀 2793·2021-10-11 10:57
閱讀 2401·2021-08-27 16:20
閱讀 1384·2019-08-30 13:03
閱讀 1562·2019-08-30 12:50
閱讀 3335·2019-08-29 14:16
閱讀 1561·2019-08-29 11:12
閱讀 1613·2019-08-28 17:53
閱讀 2892·2019-08-27 10:58