V8引擎優化機制之隱藏類和內聯緩存

咱們知道Javascript做爲一種動態語言,性能方面與c#,Java之類的靜態語言相比存在着必定的差距。而隨着Web技術的發展,對Javascript的執行效率提出愈來愈高的要求。爲了追求更好的性能,V8引擎借鑑了大量的靜態語言編譯技術來優化引擎的執行效率。好比V8引擎放棄生成中間字節碼,而是直接從AST(抽象語法樹)生成機器語言。與靜態語言不一樣, javascript的程序在執行期間須要反覆檢查數據類型。所以,V8引擎中存在兩種機制來優化這個過程。javascript

hidden class 隱藏類

對於動態類型語言來講,因爲類型的不肯定性,在方法調用過程當中,語言引擎每次都須要進行動態查詢,這就形成大量的性能消耗,從而下降程序運行的速度。大多數的Javascript 引擎會採用哈希表的方式來存取屬性和尋找方法。而爲了加快對象屬性和方法在內存中的查找速度,V8引擎引入了隱藏類(Hidden Class)的機制,起到給對象分組的做用。在初始化對象的時候,V8引擎會建立一個隱藏類,隨後在程序運行過程當中每次增減屬性,就會建立一個新的隱藏類或者查找以前已經建立好的隱藏類。每一個隱藏類都會記錄對應屬性在內存中的偏移量,從而在後續再次調用的時候能更快地定位到其位置。html

function Person(name, age) {
    this.name = name;
    this.age = age;
}

var xiaoming = new Person("xiaoming", 32);
var lisi = new Person("lisi", 20);

xiaoming.email = "xiaoming@qq.com";
xiaoming.job = "teacher";

lisi.job = "chef";
lisi.email = "lisi@qq.com";
複製代碼

觀察以上代碼,當初始化Person對象的時候, 最開始會建立一個C0的隱藏類,該類不帶有任何屬性。隨後在調用構造器函數的時候,隨着屬性的增長,引擎會生成C1,C2的過渡隱藏類,隱藏類內部會記錄屬性的偏移量(offset)。之因此存在過渡隱藏類是爲了在多個對象間可以共享隱藏類。java

這裏,注意到xiaominglisi兩個對象使用的是同一個構造函數,因此它們會共享同一個隱藏類C2。隨後雖然xiaominglisi兩個對象都添加了jobemail兩個屬性,但因爲初始化順序不一樣,會生成不一樣的隱藏類。 git

hiddenClass.png

不一樣初始化順序的對象,所生成的隱藏類是不同的。所以,在實際開發過程當中,應該儘可能保證屬性初始化的順序一致,這樣生成的隱藏類能夠獲得共享。同時,儘可能在構造函數裏就初始化全部對象成員,減小隱藏類的產生。github

inline caching 內聯緩存

僅擁有隱藏相似乎還不夠,畢竟引擎在執行過程當中還須要查找隱藏類。爲了取得更好的性能,V8引擎加入了內聯緩存(Inline Caching)技術來優化運行時查找對象及其屬性的過程。這項技術其實很古老了,最初是應用在Smalltalk虛擬機上。核心原理就是在運行過程當中,收集類型信息,從而可讓引擎在後續運行過程當中利用這些類型信息做出預判。c#

對於動態查詢優化來講,最簡單的方式是利用緩存來保留最常使用的查詢結果。每次調用對象上的方法或屬性的時候先查詢緩存,若是命中則直接使用緩存結果。若是未命中,就查詢隱藏類來獲取結果。內聯緩存也是基於這個思想。可是若是想要進一步優化查詢效率,應該怎麼作呢? 考慮到在程序中類型不多發生改變,內聯緩存技術會直接將查詢結果寫入調用方法中,來避免查詢緩存。可是萬一類型在程序執行中途發生變化了怎麼辦?對於這種狀況,內聯緩存會在直接調用以前驗證類型,這些驗證類型的代碼叫作"前導代碼"。緩存

var arr = [1, 2, 3, 4];
arr.forEach((item) => console.log(item.toString());
複製代碼

像上面這段代碼,數字1在第一次toString()方法時會發起一次動態查詢,並記錄查詢結果。當後續再調用toString方法時,引擎就能根據上次的記錄直接獲知調用點,再也不進行動態查詢操做。session

再來考慮下面這個狀況:ide

var arr = [1, '2', 3, '4'];
arr.forEach((item) => console.log(item.toString());
複製代碼

能夠看到,調用toString方法的對象類型常常發生改變,這就會致使緩存失效。爲了防止這種狀況發生,V8引擎採用了 polymorphic inline cache (PIC) 技術, 該技術不只僅只緩存最後一次查詢結果,還會緩存屢次的查詢結果(取決於記錄上限)。函數

參考資料

blog.sessionstack.com/how-javascr…

blog.sessionstack.com/how-does-ja…

github.com/sq/JSIL/wik…

richardartoul.github.io/jekyll/upda…

相關文章
相關標籤/搜索