Js引擎解析執行 閱讀筆記

Js引擎解析執行 閱讀筆記


一篇閱讀筆記
http://km.oa.com/group/2178/articles/show/145691?kmref=search&from_page=1&no=1

早期:遍歷語法樹

Js引擎最早使用的是遍歷語法樹方式
(syntax tree walker)

分為兩步

  • 詞法分析
  • 語法分析

詞法分析

i = a + b * c;
轉換
"i", "=", "a", "+", "b", "*", "c";

語法分析

生成語法樹

 執行這條語句,就是遍歷這顆語法樹的過程。遍歷語法樹的過程在程序設計上一般采用訪問者模式(vistor pattern)來實現。要遍歷這顆語法樹,只要將根節點傳給visit函數, 然后這個函數遞歸調用相應子節點的visit函數,如此反復直到葉子節點。例如,在這個例子中根節點是個賦值語句,他知道應該計算出右邊表達式的值,然后賦給左邊的地址;而在計算右邊表達式的時候,發現是一個加法表達式,于是接著遞歸計算加法表達式的值,如此遞歸進行直到這顆樹的葉子節點,然后一步步回溯,將值傳到到根節點,就完成了一次遍歷,也即完成了一次執行。
  要執行一棵語法樹,實際上是一個后序遍歷樹的過程。以上面這個例子,要計算賦值語句,先計算加法表達式,那就必須先計算乘法表達式,也就是說只有子結點計算好了之后,父節點才能計算,典型的后序遍歷。
  


中期:字節碼(bytecode)

在引擎的語境下,字節碼指的是虛擬機執行的中間指令集。
如:

  • Java編譯器把Java編譯成Java字節碼,然后在Java虛擬機中執行
  • ActiveScript,轉換成字節碼,在FLASH虛擬機中執行

分類

  • 基于棧stack-based
  • 基于寄存器register-based

如果在后序遍歷這棵樹后,生成對應的后綴記法(逆波蘭式)的操作序列,然后在執行時,直接解釋執行這后綴記法的操作序列。那么就把這種樹狀結構,變換成了一種線性結構。這種操作序列就是字節碼(bytecode),這種執行方式就是字節碼解釋方式(bytecode interpreter)。

此處輸入圖片的描述
 
傳統的字節碼設計大多是基于棧的,這種方式將所有的操作數和中間表示都保存在一個數據棧中。
如語句:c = a + b,轉換后的字節碼如下:

LOAD a  # 將a推入棧頂
LOAD b  # 將b推入棧頂
ADD     # 從棧頂彈出兩個操作數,相加后,將結果推入棧頂
STORE c  #將棧頂數據保存到C中

基于寄存器的字節碼通過寄存器(register)保存操作數。這里與匯編代碼中的寄存器是兩個概念。寄存器可以想象成是一個固定數組。上例轉換成基于寄存器的代碼如下:

ADD c, a, b   # 兩個操作數分別存在a和b中,將結果放在c中。

棧式字節碼每條的指令更短(目的地址不用顯式表示),但是總的指令條數更多。
棧式虛擬機實現比寄存器式簡單。
Flash Player的ActionScript虛擬機Tamarin、Firefox的JagerMonkey采用的是棧式設計;webkit,carakan采用寄存器方式。
字節碼是需要在虛擬機中執行的,而虛擬機的執行過程與CPU過程類似,也是取指,解碼,執行的過程。通常情況下,每個操作碼對應一段處理函數,然后通過一個無限循環加一個switch的方式進行分派。如:

switch loop

這里的vpc是一個字節碼數組的指針,作用與PC寄存器類似,稱作虛擬PC(virtual program counter)。字節碼序列直接描述要執行的動作,去除語法信息;執行一條字節碼語句,只是一次的內存訪問(取指令)加上一次間接跳轉(分派處理函數),比訪問語法樹中節點的開銷要小。因此,字節碼方式與遍歷語法樹相比在性能上有很大的提升。雖然從語法樹生成字節碼需要時間,但是這一段時間可以從直接執行字節碼所獲得的性能提升上得到補償。畢竟在實際的代碼中,不會所有的代碼都只被執行一次。而且生成了字節碼之后,就可以對于這種中間代碼進行各種優化,比如常量傳播,常量折疊,公共子表達式刪除等等。當然這些優化都是有針對性和選擇性的,畢竟優化的過程也是需要消耗時間的。而這些優化要想直接在語法樹上進行幾乎是不可能的。

Driect Threading

字節碼方式相對于遍歷語法樹已經前進了一大步,但是在分派方式上還可以再改進。Switch Loop分派方式每次處理完一條指令后,都要回到循環的開始,處理下一條,并且每次switch操作,都是一次線性搜索(現代編譯器一般都能對switch語句進行優化, 以消除線性搜索開銷,但這種優化只限于特定條件,如case的數量和值的跨度范圍等),對于一般的函數,只有有限的幾個switch case,尚可接受,但是對于虛擬機來說,有上百個switch case并且頻繁地執行,執行一條指令就需要一次線性搜索,還是太慢了。如果能用查表的方式直接跳轉,就可以省去線性搜索的過程了。于是在字節碼的分派方式上,新的改進稱作Direct Threading。

Direct
Threading,這里的threading與我們通常理解的線程沒有任何關系,可以理解成是針線中的那個“線”。以這種方式執行時,每執行完一條指令后不是回到循環的開始,而是直接跳到下一條要執行的指令地址。這種方式就比原來的Switch
Loop方式有效許多。但是要想有效的實現Direct Threading,需要用到一個gcc的擴展“Labels As
Values”,普通的goto語句的標號是在編譯時指定的,但是利用“Labels As
Values”擴展,goto語句的標號是就可以在運行時計算(這種goto語句也叫Computed
Goto),利用這個特性就可以很容易地實現Direct
Threading。(想在windows平臺用這個特性,也有幾個GCC的windows移植版本,如MinGW, Cygwin等)
右圖中的Direct Threading方式已經沒有了循環和switch分支,所有的字節碼分派就是通過“goto *vpc++”進行的。

在引入即時編譯(JIT)之前,Direct Threading方式是字節碼解釋器最有效和最塊的分派方式。對于一般的JavaScript運算,這種方式足夠用了。但是解釋執行方式肯定比不上直接執行二進制代碼。于是接下來即時編譯(JIT)技術被引入了JavaScript引擎。


現在:即時編譯Just-In-Time

字節碼指令--->本地機器碼

JIT這種技術本身很古老,可以追溯到60年代的LISP語言;現代的大部分運行時環境(runtime environment),如微軟的.NET框架和大多數的Java實現都是依賴JIT技術來提高性能。在JavaScript引擎中引入JIT是在2008年開始的。
JIT是一種提高性能的方法。通常一個程序有兩種方式執行:靜態編譯和解釋執行。靜態編譯就是在運行前先將源代碼(如c,c++)針對特定平臺(如x86,arm,mips)編譯成機器代碼,在運行時就可以直接在相應的平臺上執行;
而解釋執行則是每次運行的時候,將每條源代碼(如python, javascript)翻譯成相應的機器碼并立刻執行,并不保存翻譯后的機器碼,周而復始。可以看到解釋執行的運行效率很低,因為每次執行都需要逐句地翻譯成機器碼然后執行;而靜態編譯在運行前就編譯成相應平臺的代碼。但是靜態編譯使得平臺移植性很差,也無法實施運行時優化,而且對于動態語言(弱類型語言),變量的類型在運行前未知,很難做到靜態編譯。JIT編譯則是這兩種方式的混合,在運行時將源代碼翻譯成機器碼(這一點與解釋執行類似),但是會保存已翻譯的機器代碼,下次執行同一代碼段時無需再翻譯(這又與靜態編譯類似)
在實際的實現中,對于簡單的指令,如mov,就直接即時編譯,inline到機器碼中;對于復雜的指令,如add指令,會對它的常用方式(如操作數是數值或字符串)直接生成對應的機器碼,對于add的其他不常用情況(如一個操作數是數值,另一個是字符串)則是生成一條call本地調用
字節碼編譯成本地機器碼(JIT的過程)需要消耗執行時間,所以不是對所有代碼都會生成機器碼,而是只對熱點(hot spot)片段進行即時編譯,同時在運行中會隨時跟蹤熱點的狀態,如果熱點的程度越高(被執行得越頻繁),實施的優化也越激進。

此處輸入圖片的描述

以firefox為例,在開始執行時,將源代碼生成字節碼,然后解釋執行字節碼,在執行過程中,如果發現一條路徑多次執行(比如一個循環體),那么就標記為“HOT”,同時將這條路徑上的代碼即時編譯成機器碼,當下次再運行到這條路徑時,就直接運行機器碼。
在上圖判斷熱點的虛框中,如果一個路徑被執行了超過16次(比如“循環”迭代了超過16次),或一個函數被調用超過16次,那么就進行即時編譯;否則解釋執行。

轉載于:https://www.cnblogs.com/Ox9A82/p/7325742.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/371528.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/371528.shtml
英文地址,請注明出處:http://en.pswp.cn/news/371528.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

紅外線遙控c語言程序,紅外遙控的C程序

紅外遙控在生產和生活中應用越來越廣泛,不同的紅外遙控芯片有不同的發碼協議,但一般都是由引導碼,系統碼,鍵碼三部分組成.引導碼是告訴接收機準備接收紅外遙控碼.系統碼是識別碼,不同的遙控芯片有不同的誤別碼,以免搞錯.遙控器上不同的按鍵有不同的鍵碼,系統碼和鍵碼都是16位碼…

Retrofit2 完全解析 探索與okhttp之間的關系

轉載請標明出處: http://blog.csdn.net/lmj623565791/article/details/51304204; 本文出自:【張鴻洋的博客】 之前寫了個okhttputils的工具類,然后有很多同學詢問這個工具類和retrofit什么區別,于是上了下官網,發現其底…

不變性真的意味著線程安全嗎?

我經常閱讀有關“如果對象是不可變的,則它是線程安全的”的文章。 實際上,我從未找到過一篇讓我相信不變的意味著線程安全的文章。 即使是Brian Goetz的Java Concurrency in Practice一書中關于不變性的一本書也沒有完全令我滿意。 在這本書中&#xff0…

c語言設計 數組的知識點,C語言程序設計知識點及示例.pdf

C語言程序設計知識點及示例四川大學錦江學院C語言程序設計知識點及示例知識點1:除了復合語句而外,C語言的語句都以分號結束。示例1:C語言的簡單語句 (非復合語句語句)必須以 結束。參考答案:分號知識點2:目標程序和可執…

移動端知識匯總

參見地址: https://github.com/jtyjty99999/mobileTech 轉載于:https://www.cnblogs.com/duanyue/p/7337789.html

在移動端設置overflow:hidden禁止滾動的解決方法

如果你是將overflow:hidden用在了body上那么不管用,因為移動端是基于touch事件。 兩種解決方法: 1、為html和body同時設置height:100%;overflow:hidden; html, body{height:100%;overflow:hidden; }2、使用touchmove $(document).on(touchmove,function …

單元測試線程代碼的5個技巧

這是一些技巧,說明如何進行代碼的邏輯正確性測試(與多線程正確性相對)。 我發現本質上有兩種帶有線程代碼的刻板印象模式: 面向任務–許多短期運行的同類任務,通常在Java 5執行程序框架內運行, 面向流程–…

jsp2

D:\Software\Tomcat7\work\Catalina\localhost 是緩存目錄&#xff0c;可以刪掉隱藏域&#xff1a;頁面表單中的一個元素&#xff0c;跟文本框一樣&#xff0c;但是用戶看不到1.建立test1--form表單需要它&#xff0c;而不需要用戶看到&#xff0c;用隱藏域<body><%re…

MongoDB MapReduce 的示例。

// JavaScript source code db.runCommand({mapreduce: "page",map: function Map() {emit(this.title, // how to group{ name: this.name } // associated data point (document));},reduce: function Reduce(key, values) {//reduce用來處理group出來是多條數…

c語言長空格的代碼是什么,c語言中表示空格的是什么代碼?

分析如下&#xff1a;不是所有字符都需要轉義的&#xff0c;空格直接就敲空格&#xff0c;或者使用ASCII碼值賦值為32。空格沒有轉義字符。合法轉義字符如下&#xff1a;\a 響鈴(BEL) 、\b 退格(BS)、\f 換頁(FF)、\n 換行(LF)、\r 回車(CR)、\t 水平制表(HT)、\v 垂直制表(VT)…

使用NoSQL實現實體服務–第1部分:概述

在過去的幾周中&#xff0c;我一直在進行一些研發工作&#xff0c;以了解使用NoSQL數據庫實現實體服務 &#xff08;也稱為數據服務&#xff09;的優勢。 實體服務是托馬斯埃爾&#xff08;Thomas Erl&#xff09;的《服務技術》叢書中提出的服務分類。 它用于描述高度不可知和…

IO注意事項

read()方法返回值為什么是int? 因為字節輸入流可以操作任意類型的文件,比如圖片音頻等,這些文件底層都是以二進制形式的存儲的,如果每次讀取都返回byte,有可能在讀到中間的時候遇到111111111,那么這11111111是byte類型的-1,我們的程序是遇到-1就會停止不讀了,后面的數據就讀不…

c語言用星號輸出沙漏,《算法筆記》學習日記——3.3 圖形輸出

3.3 圖形輸出問題 A: 輸出梯形題目描述輸入一個高度h&#xff0c;輸出一個高為h&#xff0c;上底邊為h的梯形。輸入一個整數h(1<h<1000)。輸出h所對應的梯形。樣例輸入web5樣例輸出數組*********************************************思路這一類的題目都比較簡單&#xf…

JavaOne 2012:101種改進Java的方法-開發人員參與為何如此重要

Bruno Souza &#xff0c; Martijn Verburg和Heather Vancura在希爾頓酒店的大陸宴會廳4中展示了“ 101種改進Java的方法&#xff1a;開發人員參與為何如此重要”。 他們將其分為自己最熟悉的領域。 SouJava的創始人兼協調員 Souza談到了通過用戶組的更大參與。 Verberg也在倫敦…

Java組合實體模式~

組合實體模式用于EJB持久化機制。 組合實體是表示對象圖的EJB實體bean。 當組合實體更新時&#xff0c;內部依賴對象bean將自動更新為由EJB實體bean管理。 以下是組合實體Bean的參與者。 組合實體 - 它是主要的實體bean。 它可以是粗粒度的或可以包含用于持久性目的的粗粒度對象…

python中的一些小知識

在最近學習python中遇到的一些小問題匯總一下&#xff1a; 1.在windows7下安裝python3.5版本時提示安裝不了&#xff0c;缺少ServicePack1. 解決辦法是&#xff0c;打開控制面板\系統和安全\Windows Update&#xff0c;下載和更新計算機安裝&#xff0c;然后卸載以前的python版…

在Java中衡量執行時間– Spring StopWatch示例

有兩種方法可以通過使用System.currentTimeinMillis&#xff08;&#xff09;或通過使用System.nanoTime&#xff08;&#xff09; 來測量Java中經過的執行時間 。 這兩個方法可用于測量 Java中兩個方法調用或事件之間的經過時間或執行時間 。 計算經過的時間是Java程序員要做的…

c語言getch在哪個頭文件,用getch()需要頭文件嗎?

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓#include #include #include "string.h"#includeusing namespace std;struct student{ int num;char name[10];char banji[10];float score[3];struct student *next;};struct student *creat(){struct student *head,*p…

My solution for Git Client Error: Permission denied (publickey)

在使用Git客戶端的過程中遇到的問題以及解決方案分享。 我之前已經安裝Git客戶端并且使用Git開發過公司項目&#xff0c;也已經正確生成PublicKey并且添加到SSH keys on github of my account&#xff0c;但是當我想從github上克隆另一個客戶端push的代碼的時候一直報錯&#x…

OutOfMemoryError:無法創建新的本機線程–問題神秘化

正如您從我以前的教程和案例研究中可能已經看到的那樣&#xff0c;要確定和解決Java Heap Space OutOfMemoryError問題可能很復雜。 我從Java EE生產系統中觀察到的常見問題之一是OutOfMemoryError&#xff1a;無法創建新的本機線程&#xff1b; HotSpot JVM無法進一步創建新的…