V8 執行 JavaScript 的過程

👆  這是第 102 篇不摻水的原創,想要了解更多,請戳上方藍色字體:政採雲前端團隊 關注咱們吧~前端

本文首發於政採雲前端團隊博客:V8 執行 JavaScript 的過程json

https://www.zoo.team/article/the-process-of-executing-js-in-v8

前言

本文意在簡單的介紹一下 V8 執行 JS 的過程,經過了解 V8 執行 JS 的過程,知道 JS 代碼呈如今瀏覽器上到底作了什麼。固然本人也是在陸續探索 V8 ,文章中若有不當之處,還望不吝指正,理性交流。瀏覽器

衆所周知,機器(CPU)只能識別機器碼(二進制碼),對於 JS 代碼,它是識別不了的,因此當代碼成爲頁面出如今屏幕上的時候,必然是作了不少的轉譯工做。緩存

V8 執行 JavaScript 過程

如上圖所示,咱們將一步步進行拆分分析:微信

JS TO AST

在 V8 引擎拿到 JS 代碼以後,解析器(Parser)會對其進行詞法分析和語法分析。架構

詞法分析

將 JS 代碼拆分紅對應的 Token,Token 是能拆分的最小單位,固定 type 表述類型/屬性,value 表示對應的值,以下圖 Token。函數

語法分析

在進行詞法分析轉爲 Token 以後,解析器繼續根據生成的 Token 生成對應的 AST,AST 相信前端同窗並不陌生,也是熱詞之一,不管是在 Vue、React 中虛擬 DOM 的表示,或者 Babel 對 JS 轉譯的表示都是先將其轉化爲對應的 AST,解析器解析以後的 AST 結構以下圖所示:性能

能夠看到,一段極小的代碼片斷,被解析成 AST 以後複雜了不少,在圖中的 AST 還僅僅是簡化後的數據,所有的 AST 其實還有不少參數,將會更復雜。
字體

字節碼

在解析器(Parser)將 JS 代碼解析成 AST 以後,解釋器(Ignition)根據 AST 來生成字節碼(也稱中間碼)。前文提到 CPU 只能識別機器碼,對字節碼是識別不了的,這裏就衍生出一個問題,若是 CPU 識別不了字節碼,那爲何還要在中間插一步來耗費資源轉字節碼呢?效率不是很更低嗎?flex

在計算機學科裏聊效率,都逃避不了時間和空間這兩個概念,絕大部分的優化都是空間換時間和時間換空間,二者的平衡,效率如何達到最高,是一個很值得深刻研究的問題。

拿以前版本的 V8 引擎執行 JS 來講,是沒有轉字節碼這一步驟的,直接從 AST 轉成機器碼,這個過程稱爲編譯過程,因此每次拿到 JS 文件的時候,首先都會編譯,而這個過程仍是比較浪費時間的,這是一件比較頭疼的事情,須要一個解決辦法。

一個網頁第一次打開,關閉再次去打開,大部分狀況下,仍是和原來 JS 文件一致的,除非開發者修改了代碼,但這個能夠暫時不考慮,畢竟哪一個網站也不會一天閒的無聊,不停的修改,上傳替換。

緩存機器碼

按照這個思路,既然絕大多數狀況下,文件不會修改,那編譯後的機器碼能夠考慮緩存下來,這樣一來,下次再打開或者刷新頁面的時候就省去編譯的過程了,能夠直接執行了,存儲機器碼被分紅了兩種狀況,一個是瀏覽器未關閉時候,直接存儲到瀏覽器本地的內存中,一個是瀏覽器關閉了,直接存儲在磁盤上,而早期的 V8 也確實是這麼作的,典型的犧牲空間換時間。

緩存帶來的問題

思考一個問題,從上面的圖中能夠看到,一個很小的代碼片斷,轉換成 AST 以後,變大了不少,文件大了致使一個問題就是須要更大的內存來存儲,而 JS 文件轉成機器碼(即二進制文件),會比原來的 JS 文件大幾百甚至幾千倍,這就意味一個幾十 KB 的 JS 文件將會達到幾十 MB,這就很可怕,原本 Chrome 多進程架構就已經很佔用內存了,再來這一出,配置再好的電腦,也怕是無福消受 Chrome 了,畢竟使用者體驗的好壞,直接決定了一個產品在市場上是否能生存下去,儘管 V8 緩存了編譯後的代碼,減小了編譯的時間,提升了時間上的效率,但代價是內存佔用太大了,因此 Chrome 團隊是有必要優化這個問題的。

惰性編譯

固然,引進其餘技術是須要時間去開發和優化的,在一個技術架構產生的同時,必然會有劣勢方面的彌補,而早期版本的 V8 爲了解決佔用內存和啓動速度,引進了惰性編譯,那麼問題來了,惰性編譯作了什麼去提升效率的呢?

惰性編譯仍是比較容易理解的,從做用域的角度思考,ES6 以前之只有全局做用域和函數做用域,而惰性編譯的思路就是 V8 啓動的時候只編譯和緩存全局做用域的代碼,而函數做用域中的代碼,會在調用的時候去編譯,一樣函數內部編譯後的代碼同樣不會被緩存下來。

惰性編譯存在的問題

引入惰性編譯以後,在編譯速度和緩存上看來,都獲得了提高,一切看起來彷佛很完美了,對,是看起來,可是設計出來的東西,你永遠不知道使用者會怎麼使用,在 ES6 和 Vue、React 等這些沒有普及以前,絕大部分開發者都使用的是 jQuery,以及 RequireJS 等相似產品,JQ 插件各類引用,各類插件或者開發者本身封裝的方法,爲了避免污染其餘使用者的變量,通常都封裝成一個函數,這樣問題就來了,惰性編譯不會保存函數編譯後的機器碼和理解編譯函數,若是一個插件太大那等到使用函數再去編譯,編譯的時間上就會變得很慢,這至關因而開發者將惰性編譯給玩完了,路給封死了。

引入字節碼

好吧,玩不過開發者了,那 V8 團隊只好換個思路,就引入字節碼吧。首先要理解什麼是字節碼,字節碼實際上是機器碼的抽象,各類字節碼的相互構成,能夠實現 JS 所需的全部功能,固然首先一點,字節碼比機器碼佔用的內存要小不少不少,基本是機器碼所在內存的幾十甚至幾百分之一,這樣一來字節碼緩存下來所消耗的內存仍是能夠接受的。

這裏會有一個疑問,既然 CPU 不能識別字節碼,那是否是還須要將字節碼轉成機器碼呢?否則怎麼執行,答案是確定。解釋在將 AST 轉爲字節碼以後,會在執行的時候將字節碼轉成機器碼,這個執行過程確定是比直接執行機器碼要慢的,因此在執行方面,速度上會比較慢,可是 JS 源碼經過解析器轉 AST,而後再經過解釋器轉字節碼,這個過程是比編譯器直接將 JS 源碼起色器碼要快不少的,全流程看來,整個時間上是差不了多少的,可是卻減少了大量的內存佔用,何樂而不爲。

編譯器

熱代碼

在代碼中,經常會有同一部分代碼,被屢次調用,同一部分代碼若是每次都須要解釋器轉二進制代碼再去執行,效率上來講,會有些浪費,因此在 V8 模塊中會有專門的監控模塊,來監控同一代碼是否屢次被調用,若是被屢次調用,那麼就會被標記爲熱代碼,這有什麼做用呢?

優化編譯器

TurboFan (優化編譯器) 這個詞相信關注手機界的同窗並不陌生,華爲、小米等這些品牌,在近幾年產品發佈會上都會出現這個詞,主要的能力是經過軟件計算能力來優化一系列的功能,使得效率更優。

接着熱代碼繼續說,當存在熱代碼的時候,V8 會藉着 TurboFan 將爲熱代碼的字節碼轉爲機器碼並緩存下來,這樣一來,當再次調用熱代碼時,就不在須要將字節碼起色器碼,固然熱代碼相對來講仍是少部分的,因此緩存也並不會佔用太大內存,而且提高了執行效率,一樣此處也是犧牲空間換時間。

反優化

JS 語言是動態語言,很是之靈活,對象的結構和屬性在運行時是能夠發生改變的,設想一個問題,若是熱代碼在某次執行的時候,忽然其中的某個屬性被修改了,那麼編譯成機器碼的熱代碼還能繼續執行嗎?答案是確定不能。這個時候就要使用到優化編譯器的反優化了,他會將熱代碼退回到 AST 這一步,這個時候解釋器會從新解釋執行被修改的代碼,若是代碼再次被標記爲熱代碼,那麼會重複執行優化編譯器的這個步驟。

總結

從分析的過程來看,V8 對 JS 執行的過程,不只使用到了解釋器,還用到了優化編譯器。這種二者結合去處理的方式,業界稱爲 JIT (Just-In-Time)。使用這種結合的方式來處理 JS,主要是利用了 AST 造成的文件較小,而經過優化編譯器編譯後的熱代碼執行效率高,二者結合,各自發揮各自的優點,將效率儘可能提高到最大。

V8 所作的事情,遠遠不止這些,這裏也僅僅是簡單概況和分析一下主流程上所作的一些事情,若是細化到每一個點,還有不少概念,好比內聯緩存、隱藏類、快屬性、慢屬性、建立對象,以及筆者以前寫的 V8 引擎垃圾回收與內存分配 等等,所作的事情實在太多,就不一一例舉了。

看完兩件事

若是你以爲這篇內容對你挺有啓發,我想邀請你幫我兩件小事

1.點個「在看」,讓更多人也能看到這篇內容(點了在看」,bug -1 😊

2.關注公衆號「 政採雲前端團隊」,持續爲你推送精選好文

招賢納士

政採雲前端團隊(ZooTeam),一個年輕富有激情和創造力的前端團隊,隸屬於政採雲產品研發部,Base 在風景如畫的杭州。團隊現有 50 餘個前端小夥伴,平均年齡 27 歲,近 3 成是全棧工程師,妥妥的青年風暴團。成員構成既有來自於阿里、網易的「老」兵,也有浙大、中科大、杭電等校的應屆新人。團隊在平常的業務對接以外,還在物料體系、工程平臺、搭建平臺、性能體驗、雲端應用、數據分析及可視化等方向進行技術探索和實戰,推進並落地了一系列的內部技術產品,持續探索前端技術體系的新邊界。

若是你想改變一直被事折騰,但願開始能折騰事;若是你想改變一直被告誡須要多些想法,卻無從破局;若是你想改變你有能力去作成那個結果,卻不須要你;若是你想改變你想作成的事須要一個團隊去支撐,但沒你帶人的位置;若是你想改變既定的節奏,將會是「5 年工做時間 3 年工做經驗」;若是你想改變原本悟性不錯,但老是有那一層窗戶紙的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但願參與到隨着業務騰飛的過程,親手推進一個有着深刻的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我以爲咱們該聊聊。任什麼時候間,等着你寫點什麼,發給 ZooTeam@cai-inc.com


本文分享自微信公衆號 - 政採雲前端團隊(Zoo-Team)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索