日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
解讀JavaScript之V8引擎及優(yōu)化代碼的5個(gè)技巧

幾個(gè)星期前,我們開始了一系列旨在深入研究 JavaScript 及其實(shí)際工作方式的系列文章:我們認(rèn)為通過了解 JavaScript 的構(gòu)建塊以及它們?nèi)绾我黄饏f(xié)作的,你將能夠編寫更好的代碼和應(yīng)用程序。

成都創(chuàng)新互聯(lián)公司專注于企業(yè)網(wǎng)絡(luò)營銷推廣、網(wǎng)站重做改版、青山網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5建站、購物商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為青山等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

本系列的***篇文章重點(diǎn)介紹了引擎,運(yùn)行時(shí)和調(diào)用堆棧的概述。第二篇文章將深入到 Google V8 JavaScript 引擎的內(nèi)部。我們還將提供一些關(guān)于如何編寫更好的 JavaScript 代碼的快速技巧 - 我們的 SessionStack 開發(fā)團(tuán)隊(duì)在構(gòu)建產(chǎn)品時(shí)所遵循的***實(shí)踐。

概覽

JavaScript 引擎是執(zhí)行 JavaScript 代碼的程序或解釋器。 JavaScript 引擎可以作為標(biāo)準(zhǔn)解釋器或即時(shí)編譯器,它以某種形式將 JavaScript 編譯為字節(jié)碼。

下面是一個(gè)實(shí)現(xiàn)了 JavaScript 引擎的流行項(xiàng)目列表:

  1. V8? — ?開源,由 Google 開發(fā),用 C ++ 編寫
  2. Rhino ?— ?由 Mozilla 基金會管理,開源,完全用 Java 開發(fā)
  3. SpiderMonkey? —? 是***個(gè)支持 Netscape Navigator 的 JavaScript 引擎,目前正供 Firefox 使用
  4. JavaScriptCore? — ?開源,由蘋果公司為 Safari 開發(fā)
  5. KJS ?— ?KDE 的引擎,最初由 Harri Porten 為 KDE 項(xiàng)目中的 Konqueror 網(wǎng)頁瀏覽器開發(fā)
  6. Chakra (JScript9) ?— ?Internet Explorer
  7. Chakra (JavaScript)? —? Microsoft Edge
  8. Nashorn, 作為 OpenJDK 的一部分,由 Oracle Java 語言和工具組編寫
  9. JerryScript ?— 物聯(lián)網(wǎng)的輕量級引擎

為什么創(chuàng)建 V8 引擎?

由 Google 構(gòu)建的 V8 引擎是開源的,用 C ++ 編寫。 此引擎被用在 Google Chrome 中。 與其他引擎不同的是,V8 也被用于流行的 Node.js 中。

V8 最初是被設(shè)計(jì)用來提高網(wǎng)頁瀏覽器內(nèi)部 JavaScript 執(zhí)行的性能。為了獲得更快的速度,V8 將 JavaScript 代碼翻譯成更高效的機(jī)器代碼,而不是使用解釋器來翻譯代碼。它通過使用 JIT(Just-In-Time)編譯器(如 SpiderMonkey 或 Rhino(Mozilla)等許多現(xiàn)代 JavaScript 引擎)來將 JavaScript 代碼編譯為機(jī)器代碼。 這里的主要區(qū)別在于 V8 不生成字節(jié)碼或任何中間代碼。

V8 曾有兩個(gè)編譯器

在 V8 的 5.9 版本出來之前(今年早些時(shí)候發(fā)布),引擎使用了兩個(gè)編譯器:

  • full-codegen - 一個(gè)簡單而且速度非??斓木幾g器,可以生成簡單且相對較慢的機(jī)器代碼。
  • Crankshaft? - 一種更復(fù)雜(Just-In-Time)的優(yōu)化編譯器,生成高度優(yōu)化的代碼。

V8 引擎也在內(nèi)部使用多個(gè)線程:

  • 主線程完成您期望的任務(wù):獲取代碼,編譯并執(zhí)行它
  • 還有一個(gè)單獨(dú)的線程用于編譯,以便主線程可以繼續(xù)執(zhí)行,而前者正在優(yōu)化代碼
  • 一個(gè) Profiler 線程,它會告訴運(yùn)行時(shí)我們花了很多時(shí)間,讓 Crankshaft 可以優(yōu)化它們
  • 一些線程處理垃圾收集器

當(dāng)***次執(zhí)行 JavaScript 代碼時(shí),V8 利用 full-codegen 編譯器,直接將解析的 JavaScript 翻譯成機(jī)器代碼而不進(jìn)行任何轉(zhuǎn)換。這使得它可以非常快速地開始執(zhí)行機(jī)器代碼。請注意,V8 不使用中間字節(jié)碼,從而不需要解釋器。

當(dāng)你的代碼運(yùn)行了一段時(shí)間,分析器線程已經(jīng)收集了足夠的數(shù)據(jù)來判斷哪個(gè)方法應(yīng)該被優(yōu)化。

接下來,Crankshaft? 從另一個(gè)線程開始優(yōu)化。它將 JavaScript 抽象語法樹轉(zhuǎn)換為被稱為 Hydrogen 的高級靜態(tài)單分配(SSA)表示,并嘗試優(yōu)化 Hydrogen 圖。大多數(shù)優(yōu)化都是在這個(gè)級別完成的。

內(nèi)聯(lián)代碼

***個(gè)優(yōu)化是提前盡可能多地內(nèi)聯(lián)代碼。內(nèi)聯(lián)是將被調(diào)用函數(shù)的主體替換為調(diào)用站點(diǎn)(調(diào)用函數(shù)的代碼行)的過程。這個(gè)簡單的步驟使得下面的優(yōu)化更有意義。

隱藏類

JavaScript 是一種基于原型的語言:沒有類和對象而是使用克隆創(chuàng)建的。 JavaScript 也是一種動(dòng)態(tài)編程語言,這意味著屬性可以在實(shí)例化后方便地添加或從對象中移除。

大多數(shù) JavaScript 解釋器使用類似字典的結(jié)構(gòu)(基于散列函數(shù))來存儲對象屬性值在內(nèi)存中的位置。這種結(jié)構(gòu)使得在 JavaScript 中檢索一個(gè)屬性的值比在 Java 或 C# 這樣的非動(dòng)態(tài)編程語言中的計(jì)算量要大得多。在 Java 中,所有的對象屬性都是在編譯之前由一個(gè)固定的對象決定的,并且不能在運(yùn)行時(shí)動(dòng)態(tài)添加或刪除(當(dāng)然,C#的動(dòng)態(tài)類型是另一個(gè)主題)。因此,屬性的值(或指向這些屬性的指針)可以作為連續(xù)的緩沖區(qū)存儲在內(nèi)存中,每個(gè)值之間有一個(gè)固定的偏移量。偏移量的長度可以很容易地根據(jù)屬性類型來確定,而在運(yùn)行時(shí)屬性類型可以改變的 JavaScript 中這是不可能的。

由于使用字典查找內(nèi)存中對象屬性的位置效率非常低,因此 V8 使用了不同的方法:隱藏類。隱藏類與 Java 等語言中使用的固定對象(類)的工作方式類似,除了隱藏類是在運(yùn)行時(shí)創(chuàng)建的這點(diǎn)區(qū)別?,F(xiàn)在,讓我們看看他們實(shí)際的例子:

 
 
 
 
  1. function Point(x, y) {
  2.     this.x = x;
  3.     this.y = y;
  4. }
  5. var p1 = new Point(1, 2);

一旦 “new Point(1,2)” 調(diào)用發(fā)生,V8 將創(chuàng)建一個(gè)名為 “C0” 的隱藏類。

尚未為 Point 定義屬性,因此“C0”為空。

一旦***個(gè)語句 “this.x = x” 被執(zhí)行(在 “Point” 函數(shù)內(nèi)部),V8 將創(chuàng)建第二個(gè)隱藏的類,名為“C1”,它基于“C0”。 “C1”描述了可以找到屬性x的在內(nèi)存中的位置(相對于對象指針)。在這種情況下,“x”被存儲在0處,這意味著當(dāng)在內(nèi)存中將點(diǎn)對象看作一段連續(xù)存儲空間時(shí),***個(gè)地址將對應(yīng)于屬性“x”。 V8 也會用“class transition”來更新“C0”,如果一個(gè)屬性“x”被添加到一個(gè)點(diǎn)對象時(shí),隱藏類應(yīng)該從“C0”切換到“C1”。下面的點(diǎn)對象的隱藏類現(xiàn)在是“C1”。

每當(dāng)一個(gè)新的屬性被添加到一個(gè)對象時(shí),舊的隱藏類將被更新為到新的隱藏類的轉(zhuǎn)換路徑。隱藏的類轉(zhuǎn)換非常重要,因?yàn)樗鼈冊试S隱藏的類在以相同方式創(chuàng)建的對象之間共享。如果兩個(gè)對象共享一個(gè)隱藏類,并將相同的屬性添加到這兩個(gè)對象,則轉(zhuǎn)換將確保兩個(gè)對象接收相同的新隱藏類和所有優(yōu)化代碼。

當(dāng)語句 “this.y = y” 被執(zhí)行時(shí),會重復(fù)同樣的過程(在 “Point” 函數(shù)內(nèi)部,“this.x = x”語句之后)。

一個(gè)名為“C2”的新隱藏類會被創(chuàng)建,如果將一個(gè)屬性 “y” 添加到一個(gè) Point 對象(已經(jīng)包含屬性“x”),一個(gè)類轉(zhuǎn)換會添加到“C1”,則隱藏類應(yīng)該更改為“C2”,點(diǎn)對象的隱藏類更新為“C2”。

隱藏類轉(zhuǎn)換取決于將屬性添加到對象的順序。看看下面的代碼片段:

 
 
 
 
  1. function Point(x, y) {
  2.     this.x = x;
  3.     this.y = y;
  4. }
  5. var p1 = new Point(1, 2);
  6. p1.a = 5;
  7. p1.b = 6;
  8. var p2 = new Point(3, 4);
  9. p2.b = 7;
  10. p2.a = 8;

現(xiàn)在,假設(shè)對于p1和p2,將使用相同的隱藏類和轉(zhuǎn)換。那么,對于“p1”,首先添加屬性“a”,然后添加屬性“b”。然而,“p2”首先分配“b”,然后是“a”。因此,由于不同的轉(zhuǎn)換路徑,“p1”和“p2”以不同的隱藏類別結(jié)束。在這種情況下,以相同的順序初始化動(dòng)態(tài)屬性好得多,以便隱藏的類可以被重用。

內(nèi)聯(lián)緩存

V8 利用另一種被稱為內(nèi)聯(lián)緩存的技術(shù)來優(yōu)化動(dòng)態(tài)類型語言。內(nèi)聯(lián)緩存依賴于發(fā)生在相同類型的對象上的相同方法的重復(fù)調(diào)用的觀察上。內(nèi)嵌緩存的更多解釋可以在這里找到。

接下來將討論內(nèi)聯(lián)緩存的一般概念(如果您沒有時(shí)間通過上面的深入了解)。

它是怎樣工作的? V8 維護(hù)一個(gè)在最近的方法調(diào)用中作為參數(shù)傳遞的對象類型的緩存,并使用這些信息來預(yù)測將來作為參數(shù)傳遞的對象的類型。如果V8能夠很好地假定傳遞給方法的對象類型,那么它可以繞過如何訪問對象的屬性的過程,而是將之前查找到的信息用于對象的隱藏類。

那么隱藏類和內(nèi)聯(lián)緩存的概念如何相關(guān)呢?無論何時(shí)在特定對象上調(diào)用方法時(shí),V8 引擎都必須執(zhí)行對該對象的隱藏類的查找,以確定訪問特定屬性的偏移量。在同一個(gè)隱藏類的兩次成功的調(diào)用之后,V8 省略了隱藏類的查找,并簡單地將該屬性的偏移量添加到對象指針本身。對于該方法的所有下一次調(diào)用,V8 引擎都假定隱藏的類沒有更改,并使用從以前的查找存儲的偏移量直接跳轉(zhuǎn)到特定屬性的內(nèi)存地址。這大大提高了執(zhí)行速度。

內(nèi)聯(lián)緩存也是為什么相同類型的對象可以共享隱藏類非常重要的原因。如果你創(chuàng)建了兩個(gè)相同類型的對象和不同的隱藏類(就像我們之前的例子中那樣),V8 將不能使用內(nèi)聯(lián)緩存,因?yàn)榧词箖蓚€(gè)對象是相同的類型,它們相應(yīng)的隱藏類為其屬性分配不同的偏移量。

這兩個(gè)對象基本相同,但“a”和“b”屬性的創(chuàng)建順序不同。

編譯成機(jī)器碼

一旦 Hydrogen 圖被優(yōu)化,Crankshaft 將其降低到稱為 Lithium 的較低級表示。大部分的 Lithium 實(shí)現(xiàn)都是特定于架構(gòu)的。寄存器分配往往發(fā)生在這個(gè)級別。

***,Lithium 被編譯成機(jī)器碼。然后就是 OSR :on-stack replacement(堆棧替換)。在我們開始編譯和優(yōu)化一個(gè)明確的長期運(yùn)行的方法之前,我們可能會運(yùn)行堆棧替換。 V8 不只是緩慢執(zhí)行堆棧替換,并再次開始優(yōu)化。相反,它會轉(zhuǎn)換我們擁有的所有上下文(堆棧,寄存器),以便在執(zhí)行過程中切換到優(yōu)化版本上。這是一個(gè)非常復(fù)雜的任務(wù),考慮到除了其他優(yōu)化之外,V8 最初還將代碼內(nèi)聯(lián)。 V8 不是唯一能夠做到的引擎。

有一種叫做去優(yōu)化的保護(hù)措施來做出相反的變換,并且在假設(shè)引擎優(yōu)化無效的情況下,還原回非優(yōu)化的代碼。

垃圾收集

對于垃圾收集,V8 采用了傳統(tǒng)的分代式掃描方式來清理老一代。標(biāo)記階段應(yīng)該停止 JavaScript 的執(zhí)行。為了控制 GC 成本并使執(zhí)行更加穩(wěn)定,V8 使用了漸進(jìn)式標(biāo)記:而不是走遍整個(gè)堆內(nèi)容,試圖標(biāo)記每一個(gè)可能的對象。它只走一部分堆內(nèi)容,然后恢復(fù)正常執(zhí)行。下一個(gè) GC 將從先前堆走過的地方繼續(xù)執(zhí)行。這允許在正常執(zhí)行期間非常短的暫停。如前所述,掃描階段由不同的線程處理。

Ignition 和 TurboFan

隨著 2017 年早些時(shí)候 V8 5.9 的發(fā)布,一個(gè)新的執(zhí)行管道被引入。這個(gè)新的管道在實(shí)際的 JavaScript 應(yīng)用程序中實(shí)現(xiàn)了更大的性能改進(jìn)和顯著的內(nèi)存節(jié)省。

新的執(zhí)行流程是建立在 Ignition( V8 的解釋器)和 TurboFan( V8 的***優(yōu)化編譯器)之上的。

你可以查看 V8 團(tuán)隊(duì)關(guān)于這個(gè)話題的博客文章。

自從 V8 5.9 版本問世以來,由于 V8 團(tuán)隊(duì)一直努力跟上新的 JavaScript 語言特性以及這些特性所需要的優(yōu)化,V8 團(tuán)隊(duì)已經(jīng)不再使用 full-codegen 和 Crankshaft(自 2010 年以來為 V8 技術(shù)所服務(wù))。

這意味著 V8 整體上將有更簡單和更易維護(hù)的架構(gòu)。

在 Web 和 Node.js 性能上的提升

這些改進(jìn)僅僅是一個(gè)開始。新的 Ignition 和 TurboFan 管道為進(jìn)一步的優(yōu)化鋪平了道路,這將在未來幾年提高 JavaScript 性能,縮小 V8 在 Chrome 和 Node.js 中的占用空間。

***,這里有一些關(guān)于如何編寫優(yōu)化的、更好的 JavaScript 的技巧。你可以很容易地從上面的內(nèi)容中得到這些,不過,這里有一個(gè)為你提供便利的總結(jié):

如何編寫優(yōu)化的 JavaScript

  1. 對象屬性的順序:始終以相同的順序?qū)嵗瘜ο髮傩?,以便共享的隱藏類和隨后優(yōu)化的代碼可以共享之。
  2. 動(dòng)態(tài)屬性:在實(shí)例化之后向?qū)ο筇砑訉傩詫?qiáng)制執(zhí)行隱藏的類更改,并降低之前隱藏類所優(yōu)化的所有方法的執(zhí)行速度。相反,在其構(gòu)造函數(shù)中分配所有對象的屬性。
  3. 方法:重復(fù)執(zhí)行相同方法的代碼將比僅執(zhí)行一次的多個(gè)不同方法(由于內(nèi)聯(lián)緩存)的代碼運(yùn)行得更快。
  4. 數(shù)組:避免稀疏數(shù)組,其中鍵值不是自增的數(shù)字。并沒有存儲所有元素的稀疏數(shù)組是哈希表。這種數(shù)組中的元素訪問開銷較高。另外,盡量避免預(yù)分配大數(shù)組。***是按需增長。***,不要?jiǎng)h除數(shù)組中的元素。這會使鍵值變得稀疏。
  5. 標(biāo)記值:V8 使用 32 位表示對象和數(shù)值。由于數(shù)值是 31 位的,它使用了一位來區(qū)分它是一個(gè)對象(flag = 1)還是一個(gè)稱為 SMI(SMall Integer)整數(shù)(flag = 0)。那么,如果一個(gè)數(shù)值大于 31 位,V8會將該數(shù)字裝箱,把它變成一個(gè)雙精度數(shù),并創(chuàng)建一個(gè)新的對象來存放該數(shù)字。盡可能使用 31 位有符號數(shù)字,以避免對 JS 對象的高開銷的裝箱操作。

我們在 SessionStack 中試圖編寫高度優(yōu)化的 JavaScript 代碼時(shí)遵循這些***實(shí)踐。 原因是,一旦將 SessionStack 集成到你的產(chǎn)品級的 Web 應(yīng)用程序中,它就會開始記錄所有的東西:所有的 DOM 更改、用戶交互、JavaScript 異常、堆棧跟蹤、網(wǎng)絡(luò)請求失敗、調(diào)試消息等。

通過 SessionStack ,你可以以視頻的方式重現(xiàn)問題,并查看發(fā)生在用戶身上的所有事情。所有這些都必須在對你的網(wǎng)絡(luò)應(yīng)用程序的性能沒有任何影響的情況下進(jìn)行的。

這有一個(gè)免費(fèi)的方案,所以你可以試試看。


網(wǎng)站題目:解讀JavaScript之V8引擎及優(yōu)化代碼的5個(gè)技巧
轉(zhuǎn)載來源:http://m.5511xx.com/article/cdejphg.html