V8 引擎如何執行JS,以前看過 Webkit 技術內幕,也只是蜻蜓點水。並無深刻理解,忽然看到這篇文章,翻譯之How does the Google V8 engine work?html
Google V8
引擎是如何工做的?這是一個很是好的問題,這裏有少量流出的官方文檔來說解,到底 V8
內部都作了什麼。我會把我知道的東西分享給你(你須要本身猜,哪部分我給拿掉了),還有不少有用的地址去幫助你明白這些內容。git
V8
有兩個編譯器:github
js
編譯成簡單可是很慢的機械碼,叫作 full-codegen
。Crankshaft
。V8
裏面也使用了一些線程:緩存
profiler
線程(不知道還有沒有了,可是會有一個一樣職責的線程存在),用於發現執行過程當中哪一個方法耗費了大量時間,這樣 Crankshaft
就能夠優化這些代碼。GC
處理的線程(譯者注:這裏是一些用於 GC
的線程,不止一個線程用於垃圾回收)。最開始執行你的代碼的時候,V8
開始使用 full-codegen
,full-codegen
直接將 JS
代碼解釋成機械碼,沒有作任何轉化。這可讓 V8
快速執行機械碼。注意,V8
並不使用中間字節碼,所以也就再也不須要轉譯處理。架構
當你的代碼被執行的時候,profiler
線程有足夠的數據來找出哪些方法須要被優化。我不肯定 V8
如何選擇使用哪一個線程作的優化,簡單起見,咱們就認爲它用的主線程吧。ide
主線程經過中止正在執行的代碼(也許就在須要優化的方法這裏),開始使用 Crankshaft
進行優化。JS
代碼首先會被編譯成一種叫作 Hydrogen
的高級描述,它是控制流圖的靜態單賦值表示。大多數優化都是在這個級別完成的。oop
首先,對儘量多的代碼進行內聯,這使得優化變得更有意義。而後進行類型轉化。這個優化移除了打包和拆包的處理,能夠認爲是執行了不少指令。這意味着,若是你的代碼在操做整數,而且沒有作類型轉換,好比轉換成 string
,double
這些,那麼它會跑的很快。內聯緩存會在這個階段起到很是重要的做用,提供了類型判斷。就像你猜到的那樣,咱們須要當心類型轉換:若是你但願一個變量是一個整數,可是過一會卻被修改爲了其它類型,那麼你的假設就失敗了,那麼一次從新編譯就在所不免了。還有其它的優化,好比 loop-invariant code motion
(譯者注:貌似是講將循環內不變化的代碼移到外面,減小每一次循環執行的代碼數量),移除死代碼(譯者注:不被執行的代碼也要移除,不然 V8
始終都要要對這些代碼處理的,帶來了額外負擔)等。post
一旦 Hydrogen graph
被優化,Crankshaft
會下降它到一個低級別的描述,叫作 Lithium
。大部分的 Lithium
執行於特定架構。分配寄存器就是在這個級別進行的。性能
最終,Lithium
被編譯成機械碼。而後一些叫作 OSR
(on-stack replacement
)的事情就發生了。記住,在咱們開始編譯和優化運行耗時較長的方法以前,咱們喜歡先執行它。咱們不要忘記咱們剛剛放慢了執行,而後開始執行優化後的代碼。相反,咱們將要轉換全部的上下文,所以咱們才能在執行的中間過程當中選擇執行優化後的代碼。我讓大家感到複雜,提醒一下,其它的優化中,咱們內聯了一些東西。V8
並非惟一一個這麼作的虛擬機,可是我發現一些比較瘋狂的地方。有一些保護機制叫 -- 去優化,在作相反的事情,而且會在一些假設的特定狀況下反轉一些優化後的代碼。優化
還有。就是編譯/執行部分。我忘了提到 GC
,不過這很短,由於我對它不太瞭解。
對於垃圾收集來講,V8
使用了傳統的方法,採用標記計數的方式來進行垃圾收集。標記階段必須中止 JavaScript
執行。爲了控制 GC
成本,使執行更加穩定,V8
採用增量標記。這就是說,他們不是在堆中試圖標記每個可能的對象,而是處理一部分堆,而後恢復正常的執行。下一個 GC
中止執行代碼的時候處理以前未處理的堆。這容許很是短的暫停。如前所述,掃描階段由單獨的線程來處理。
floitsch.blogspot.de/2012/04/opt…
還有這裏源碼