摘要:內(nèi)存結(jié)構(gòu)與屬性訪問詳解從屬于筆者的前端入門與工程實(shí)踐,推薦閱讀我的前端之路工具化與工程化。內(nèi)存結(jié)構(gòu)與屬性訪問上世紀(jì)九十年代,隨著網(wǎng)景瀏覽器的發(fā)行,首次進(jìn)入人們的視線。
V8 Object 內(nèi)存結(jié)構(gòu)與屬性訪問V8 Object 內(nèi)存結(jié)構(gòu)與屬性訪問詳解從屬于筆者的Web 前端入門與工程實(shí)踐,推薦閱讀2016-我的前端之路:工具化與工程化。更多關(guān)于 JavaScript 引擎文章參考這里。
上世紀(jì)九十年代,隨著網(wǎng)景瀏覽器的發(fā)行,JavaScript 首次進(jìn)入人們的視線。之后隨著 AJAX 的大規(guī)模應(yīng)用與富客戶端、單頁應(yīng)用時(shí)代的到來,JavaScript 在 Web 開發(fā)中占據(jù)了越來越重要的地位。在早期的 JavaScript 引擎中,性能越發(fā)成為了開發(fā)網(wǎng)頁應(yīng)用的瓶頸。而 V8 引擎設(shè)計(jì)的目標(biāo)就是為了保證大型 JavaScript 應(yīng)用的執(zhí)行效率,在很多測試中可以明顯發(fā)現(xiàn) V8 的性能優(yōu)于 JScript (Internet Explorer), SpiderMonkey (Firefox), 以及 JavaScriptCore(Safari). 根據(jù) V8 的官方文檔介紹,其主要是從屬性訪問、動(dòng)態(tài)機(jī)器碼生成以及高效的垃圾回收這三個(gè)方面著手性能優(yōu)化。Obejct 當(dāng)屬 JavaScript 最重要的數(shù)據(jù)類型之一,本文我們對(duì)其內(nèi)部結(jié)構(gòu)進(jìn)行詳細(xì)闡述。其繼承關(guān)系圖如下所示:
在 V8 中新分配的 JavaScript 對(duì)象結(jié)構(gòu)如下所示:
[ class / map ] -> ... ; 指向內(nèi)部類 [ properties ] -> [empty array] [ elements ] -> [empty array] ; 數(shù)值類型名稱的屬性 [ reserved #1 ] - [ reserved #2 ] | [ reserved #3 ] }- in object properties,即預(yù)分配的內(nèi)存空間 ............... | [ reserved #N ] -/
在創(chuàng)建新的對(duì)象時(shí),V8 會(huì)創(chuàng)建某個(gè)預(yù)分配的內(nèi)存區(qū)域來存放所謂的 in-object 屬性,預(yù)分配區(qū)域的大小由構(gòu)造函數(shù)中的參數(shù)數(shù)目決定(this.field = expr)。當(dāng)你打算向?qū)ο笾刑砑幽硞€(gè)新屬性時(shí),V8 首先會(huì)嘗試放入所謂的 in-order 槽位中,當(dāng) in-object 槽位過載之后,V8 會(huì)嘗試將新的屬性添加到 out-of-object 屬性列表。而屬性名與屬性下標(biāo)的映射關(guān)系即存放在所謂隱藏類中,譬如{ a: 1, b: 2, c: 3, d: 4}對(duì)象的存儲(chǔ)方式可能如下:
[ class ] -> [a: in obj #1, b: in obj #2, c: out obj #1, d: out obj #2] [ properties ] -> [ 3 ][ 4 ] ; this is linear array [ elements ] [ 1 ] [ 2 ]
隨著屬性數(shù)目的增加,V8 會(huì)轉(zhuǎn)回到傳統(tǒng)的字典模式/哈希表模式:
[ class ] -> [ OBJECT IS IN DICTIONARY MODE ] [ properties ] -> [a: 1, b: 2, c: 3, d: 4, e: 5] ; this is classical hash table [ elements ]Reference
V8 Design Elements
A tour of V8: object representation
Demystifying v8 and JavaScript Performance
V8 Docs:Object Class Reference
How does V8 manage the memory of object instances?
Property Name:屬性名作為動(dòng)態(tài)語言,JavaScript 允許我們以非常靈活的方式來定義對(duì)象,譬如:
obj.prop obj["prop"]
參照 JavaScript 定義規(guī)范中的描述,屬性名恒為字符串,即使你使用了某個(gè)非字符串的名字,也會(huì)隱式地轉(zhuǎn)化為字符串類型。譬如你創(chuàng)建的是個(gè)數(shù)組,以數(shù)值下標(biāo)進(jìn)行訪問,然而 V8 還是將其轉(zhuǎn)化為了字符串再進(jìn)行索引,因此以下的方式就會(huì)獲得相同的效果:
obj[1]; // obj["1"]; // names for the same property obj[1.0]; // var o = { toString: function () { return "-1.5"; } }; obj[-1.5]; // also equivalent obj[o]; // since o is converted to string
而 JavaScript 中的 Array 只是包含了額外的length屬性的對(duì)象而已,length會(huì)返回當(dāng)前最大下標(biāo)加一的結(jié)果(此時(shí)字符串下標(biāo)會(huì)被轉(zhuǎn)化為數(shù)值類型計(jì)算):
var a = new Array(); a[100] = "foo"; a.length; //101 a[undefined] = "a"; a.length; //0
Function本質(zhì)上也是對(duì)象,只不過length屬性會(huì)返回參數(shù)的長度而已:
> a = ()=>{} [Function: a] > a.length 0 > a = (b)=>{} [Function: a] > a.length 1In-Object Properties & Fast Property Access:對(duì)象內(nèi)屬性與訪問優(yōu)化
作為動(dòng)態(tài)類型語言,JavaScript 中的對(duì)象屬性可以在運(yùn)行時(shí)動(dòng)態(tài)地增刪,意味著整個(gè)對(duì)象的結(jié)構(gòu)會(huì)頻繁地改變。大部分 JavaScript 引擎傾向于使用字典類型的數(shù)據(jù)結(jié)構(gòu)來存放對(duì)象屬性( Object Properties),每次進(jìn)行屬性訪問的時(shí)候引擎都需要在內(nèi)層中先動(dòng)態(tài)定位屬性對(duì)應(yīng)的下標(biāo)地址然后讀取值。這種方式實(shí)現(xiàn)上比較容易,但是會(huì)導(dǎo)致較差的性能表現(xiàn)。其他的類似于 Java 與 Smalltalk 這樣的靜態(tài)語言中,成員變量在編譯階段即確定了其在內(nèi)存中的固定偏移地址,進(jìn)行屬性訪問的時(shí)候只需要單指令從內(nèi)存中加載即可。而 V8 則利用動(dòng)態(tài)創(chuàng)建隱藏內(nèi)部類的方式動(dòng)態(tài)地將屬性的內(nèi)存地址記錄在對(duì)象內(nèi),從而提升整體的屬性訪問速度??偨Y(jié)而言,每當(dāng)為某個(gè)對(duì)象添加新的屬性時(shí),V8 會(huì)自動(dòng)修正其隱藏內(nèi)部類。我們先通過某個(gè)實(shí)驗(yàn)來感受下隱藏類的存在:
var PROPERTIES = 10000000; var o = {}; var start = +new Date; for (var i = 0; i < PROPERTIES; i++) { o[i] = i; } console.log(+new Date - start); function O(size) { for (var i = 0; i < size; i++) { this[i] = null; } } var o = new O(PROPERTIES); var start = +new Date; for (var i = 0; i < PROPERTIES; i++) { o[i] = i; } console.log(+new Date - start); class OClass { constructor(size){ for (var i = 0; i < size; i++) { this[i] = null; } } } var o = new OClass(PROPERTIES); var start = +new Date; for (var i = 0; i < PROPERTIES; i++) { o[i] = i; } console.log(+new Date - start);
該程序的執(zhí)行結(jié)果如下:
// Babel 下結(jié)果 385 37 49 // Chrome 下結(jié)果 416 32 31
第一種實(shí)現(xiàn)中,每次為對(duì)象o設(shè)置新的屬性時(shí),V8 都會(huì)創(chuàng)建新的隱藏內(nèi)部類(內(nèi)部稱為 Map)來存儲(chǔ)新的內(nèi)存地址以優(yōu)化屬性查找速度。而第二種實(shí)現(xiàn)時(shí),我們?cè)趧?chuàng)建新的對(duì)象時(shí)即初始化了內(nèi)部類,這樣在賦值屬性時(shí) V8 以及能夠高性能地定位這些屬性。第三種實(shí)現(xiàn)則是用的 ES6 Class,在純正的 V8 下性能最好。接下來我們具體闡述下隱藏類的工作原理,假設(shè)我們定義了描述點(diǎn)的函數(shù):
function Point(x, y) { this.x = x; this.y = y; }
當(dāng)我們執(zhí)行new Point(x,y)語句時(shí),V8 會(huì)創(chuàng)建某個(gè)新的Point對(duì)象。創(chuàng)建的過程中,V8 首先會(huì)創(chuàng)建某個(gè)所謂C0的隱藏內(nèi)部類,因?yàn)樯形礊閷?duì)象添加任何屬性,此時(shí)隱藏類還是空的:
接下來調(diào)用首個(gè)賦值語句this.x = x;為當(dāng)前Point對(duì)象創(chuàng)建了新的屬性x,此時(shí) V8 會(huì)基于C0創(chuàng)建另一個(gè)隱藏類C1來替換C0,然后在C1中存放對(duì)象屬性x的內(nèi)存位置信息:
這里從C0到C1的變化稱為轉(zhuǎn)換(Transitions),當(dāng)我們?yōu)橥粋€(gè)類型的對(duì)象添加新的屬性時(shí),并不是每次都會(huì)創(chuàng)建新的隱藏類,而是多個(gè)對(duì)象會(huì)共用某個(gè)符合轉(zhuǎn)換條件的隱藏類。接下來繼續(xù)執(zhí)行this.y = y 這一條語句,會(huì)為Point對(duì)象創(chuàng)建新的屬性。此時(shí) V8 會(huì)進(jìn)行以下步驟:
基于C1創(chuàng)建另一個(gè)隱藏類C1,并且將關(guān)于屬性y的位置信息寫入到C2中。
更新C1為其添加轉(zhuǎn)換信息,即當(dāng)為Point對(duì)象添加屬性 y 時(shí),應(yīng)該轉(zhuǎn)換到隱藏類 C2。
整個(gè)過程的偽代碼描述如下:
Reused Hidden Class:重復(fù)使用的隱藏類Class C0 "x": TRANSITION to C1 at offset 0 this.x = x; Class C1 "x": FIELD at offset 0 "y": TRANSITION to C2 at offset 1 this.y = y; Map C2 "x": FIELD at offset 0 "y": FIELD at offset 1
我們?cè)谏衔闹刑峒?,如果每次添加新的屬性時(shí)都創(chuàng)建新的隱藏類無疑是極大的性能浪費(fèi),實(shí)際上當(dāng)我們?cè)俅蝿?chuàng)建新的Point對(duì)象時(shí),V8 并不會(huì)創(chuàng)建新的隱藏類而是使用已有的,過程描述如下:
初始化新的Point對(duì)象,并將隱藏類指向C0。
添加x屬性時(shí),遵循隱藏類的轉(zhuǎn)換原則指向到C1 , 并且根據(jù)C1指定的偏移地址寫入x。
添加y屬性時(shí),遵循隱藏類的轉(zhuǎn)換原則指向到C2,并且根據(jù)C2指定的偏移地址寫入y。
另外我們?cè)谏衔囊枣湵淼姆绞矫枋鲛D(zhuǎn)換,實(shí)際上真實(shí)場景中 V8 會(huì)以樹的結(jié)構(gòu)來描述轉(zhuǎn)換及其之間的關(guān)系,這樣就能夠用于類似于下面的屬性一致而賦值順序顛倒的場景:
function Point(x, y, reverse) { if (reverse) { this.x = x; this.y = y; } else { this.y = x; this.x = y; } }Methods & Prototypes:方法與原型
JavaScript 中并沒有類的概念(語法糖除外),因此對(duì)于方法的調(diào)用處理會(huì)難于 C++ 或者 Java。下面這個(gè)例子中,distance方法可以被看做Point的普通屬性之一,不過其并非原始類型的數(shù)據(jù),而是指向了另一個(gè)函數(shù):
function Point(x, y) { this.x = x; this.y = y; this.distance = PointDistance; } function PointDistance(p) { var dx = this.x - p.x; var dy = this.y - p.y; return Math.sqrt(dx*dx + dy*dy); }
如果我們像上文介紹的普通的 in-object 域一樣來處理distance屬性,那么無疑會(huì)帶來較大的內(nèi)存浪費(fèi),畢竟每個(gè)對(duì)象都要存放一段外部函數(shù)引用(Reference 的內(nèi)存占用往往大于原始類型)。C++ 中則是以指向多個(gè)虛函數(shù)的虛函數(shù)表(V-Tables)解決這個(gè)問題。每個(gè)包含虛函數(shù)的類的實(shí)例都會(huì)指向這個(gè)虛函數(shù)表,當(dāng)調(diào)用某個(gè)虛函數(shù)時(shí),程序會(huì)自動(dòng)從虛函數(shù)表中加載該函數(shù)的地址信息然后轉(zhuǎn)向到該地址調(diào)用。V8 中我們已經(jīng)使用了隱藏類這一共享數(shù)據(jù)結(jié)構(gòu),因此可以很方便地改造下就可以。我們引入了所謂 Constant Functions 的概念,某個(gè) Constant Function 即代表了對(duì)象中僅包含某個(gè)名字,而具體的屬性值存放在描述符本身的概念:
Class C0 "x": TRANSITION to C1 at offset 0 this.x = x; Class C1 "x": FIELD at offset 0 "y": TRANSITION to C2 at offset 1 this.y = y; Class C2 "x": FIELD at offset 0 "y": FIELD at offset 1 "distance": TRANSITION to C3 this.distance = PointDistance; Class C3 "x": FIELD at offset 0 "y": FIELD at offset 1 "distance": CONSTANT_FUNCTION
注意,在這里如果我們將PointDistance 重定義指向了其他函數(shù),那么這個(gè)轉(zhuǎn)換也會(huì)自動(dòng)失效,V8 會(huì)創(chuàng)建新的隱藏類。另一種解決這個(gè)問題的方法就是使用原型,每個(gè)構(gòu)造函數(shù)都會(huì)有所謂的Prototype屬性,該屬性會(huì)自動(dòng)成為對(duì)象的原型鏈上的一環(huán),上面的例子可以改寫為以下方式:
function Point(x, y) { this.x = x; this.y = y; } Point.prototype.distance = function(p) { var dx = this.x - p.x; var dy = this.y - p.y; return Math.sqrt(dx*dx + dy*dy); } ... var u = new Point(1, 2); var v = new Point(3, 4); var d = u.distance(v);
V8 同樣會(huì)把原型鏈上的方法在隱藏類中映射為 Constant Function 描述符,而調(diào)用原型方法往往會(huì)比調(diào)用自身方法慢一點(diǎn),畢竟引擎不僅要去掃描自身的隱藏類,還要去掃描原型鏈上對(duì)象的隱藏類才能得知真正的函數(shù)調(diào)用地址。不過這個(gè)不會(huì)對(duì)于代碼的性能造成明顯的影響,因此寫代碼的時(shí)候也不必小心翼翼的避免這個(gè)。
Dictionary Mode對(duì)于復(fù)雜屬性的對(duì)象,V8 會(huì)使用所謂的字典模式(Dictionary Mode)來存儲(chǔ)對(duì)象,也就是使用哈希表來存放鍵值信息,這種方式存儲(chǔ)開銷會(huì)小于上文提到的包含了隱藏類的方式,不過查詢速度會(huì)遠(yuǎn)小于前者。初始狀態(tài)下,哈希表中的所有的鍵與值都被設(shè)置為了undefined,當(dāng)插入新的數(shù)據(jù)時(shí),計(jì)算得出的鍵名的哈希值的低位會(huì)被當(dāng)做初始的存儲(chǔ)索引地址。如果此地址已經(jīng)被占用了,V8 會(huì)嘗試向下一個(gè)地址進(jìn)行插入,直到插入成功,偽代碼表述如下:
// 插入 insert(table, key, value): table = ensureCapacity(table, length(table) + 1) code = hash(key) n = capacity(table) index = code (mod n) while getKey(table, index) is not undefined: index += 1 (mod n) set(table, index, key, value) //查找 lookup(table, key): code = hash(key) n = capacity(table) index = code (mod n) k = getKey(table, index) while k is not null or undefined and k != key: index += 1 (mod n) k = getKey(table, index) if k == key: return getValue(table, index) else: return undefined
盡管計(jì)算鍵名哈希值與比較的速度會(huì)比較快,但是每次讀寫屬性的時(shí)候都進(jìn)行這么多步驟無疑會(huì)大大拉低速度,因此 V8 盡可能地會(huì)避免使用這種存儲(chǔ)方式。
Fast Elements:數(shù)值下標(biāo)的屬性V8 中將屬性名為非負(fù)整數(shù)(0、1、2……)的屬性稱為Element,每個(gè)對(duì)象都有一個(gè)指向Element數(shù)組的指針,其存放和其他屬性是分開的。注意,隱藏類中并不包含 Element 的描述符,但可能包含其它有著不同 Element 類型的同一種隱藏類的轉(zhuǎn)換描述符。大多數(shù)情況下,對(duì)象都會(huì)有 Fast Element,也就是說這些 Element 以連續(xù)數(shù)組的形式存放。有三種不同的 Fast Element:
Fast small integers
Fast doubles
Fast values
根據(jù)標(biāo)準(zhǔn),JavaScript 中的所有數(shù)字都理應(yīng)以64位浮點(diǎn)數(shù)形式出現(xiàn)。因此 V8 盡可能以31位帶符號(hào)整數(shù)來表達(dá)數(shù)字(最低位總是0,這有助于垃圾回收器區(qū)分?jǐn)?shù)字和指針)。因此含有Fast small integers類型的對(duì)象,其 Element 類型只會(huì)包含這樣的數(shù)字。如果需要存儲(chǔ)小數(shù)、大整數(shù)或其他特殊值,如-0,則需要將數(shù)組提升為 Fast doubles。于是這引入了潛在的昂貴的復(fù)制-轉(zhuǎn)換操作,但通常不會(huì)頻繁發(fā)生。Fast doubles 仍然是很快的,因?yàn)樗械臄?shù)字都是無封箱存儲(chǔ)的。但如果我們要存儲(chǔ)的是其他類型,比如字符串或者對(duì)象,則必須將其提升為普通的 Fast Element 數(shù)組。
JavaScript 不提供任何確定存儲(chǔ)元素多少的辦法。你可能會(huì)說像這樣的辦法,new Array(100),但實(shí)際上這僅僅針對(duì)Array構(gòu)造函數(shù)有用。如果你將值存在一個(gè)不存在的下標(biāo)上,V8會(huì)重新開辟更大的內(nèi)存,將原有元素復(fù)制到新內(nèi)存。V8 可以處理帶空洞的數(shù)組,也就是只有某些下標(biāo)是存有元素,而期間的下標(biāo)都是空的。其內(nèi)部會(huì)安插特殊的哨兵值,因此試圖訪問未賦值的下標(biāo),會(huì)得到undefined。當(dāng)然,F(xiàn)ast Element 也有其限制。如果你在遠(yuǎn)遠(yuǎn)超過當(dāng)前數(shù)組大小的下標(biāo)賦值,V8 會(huì)將數(shù)組轉(zhuǎn)換為字典模式,將值以哈希表的形式存儲(chǔ)。這對(duì)于稀疏數(shù)組來說很有用,但性能上肯定打了折扣,無論是從轉(zhuǎn)換這一過程來說,還是從之后的訪問來說。如果你需要復(fù)制整個(gè)數(shù)組,不要逆向復(fù)制(索引從高到低),因?yàn)檫@幾乎必然觸發(fā)字典模式。
// 這會(huì)大大降低大數(shù)組的性能 function copy(a) { var b = new Array(); for (var i = a.length - 1; i >= 0; i--) b[i] = a[i]; return b; }
由于普通的屬性和數(shù)字式屬性分開存放,即使數(shù)組退化為字典模式,也不會(huì)影響到其他屬性的訪問速度(反之亦然)。
Object 代碼聲明// https://v8docs.nodesource.com/node-7.2/d4/da0/v8_8h_source.html#l02660 class V8_EXPORT Object : public Value { public: V8_DEPRECATE_SOON("Use maybe version", bool Set(Localkey, Local value)); V8_WARN_UNUSED_RESULT Maybe Set(Local context, Local key, Local value); V8_DEPRECATE_SOON("Use maybe version", bool Set(uint32_t index, Local value)); V8_WARN_UNUSED_RESULT Maybe Set(Local context, uint32_t index, Local value); // Implements CreateDataProperty (ECMA-262, 7.3.4). // // Defines a configurable, writable, enumerable property with the given value // on the object unless the property already exists and is not configurable // or the object is not extensible. // // Returns true on success. V8_WARN_UNUSED_RESULT Maybe CreateDataProperty(Local context, Local key, Local value); V8_WARN_UNUSED_RESULT Maybe CreateDataProperty(Local context, uint32_t index, Local value); // Implements DefineOwnProperty. // // In general, CreateDataProperty will be faster, however, does not allow // for specifying attributes. // // Returns true on success. V8_WARN_UNUSED_RESULT Maybe DefineOwnProperty( Local context, Local key, Local value, PropertyAttribute attributes = None); // Sets an own property on this object bypassing interceptors and // overriding accessors or read-only properties. // // Note that if the object has an interceptor the property will be set // locally, but since the interceptor takes precedence the local property // will only be returned if the interceptor doesn"t return a value. // // Note also that this only works for named properties. V8_DEPRECATED("Use CreateDataProperty / DefineOwnProperty", bool ForceSet(Local key, Local value, PropertyAttribute attribs = None)); V8_DEPRECATE_SOON("Use CreateDataProperty / DefineOwnProperty", Maybe ForceSet(Local context, Local key, Local value, PropertyAttribute attribs = None)); V8_DEPRECATE_SOON("Use maybe version", Local Get(Local key)); V8_WARN_UNUSED_RESULT MaybeLocal Get(Local context, Local key); V8_DEPRECATE_SOON("Use maybe version", Local Get(uint32_t index)); V8_WARN_UNUSED_RESULT MaybeLocal Get(Local context, uint32_t index); V8_DEPRECATED("Use maybe version", PropertyAttribute GetPropertyAttributes(Local key)); V8_WARN_UNUSED_RESULT Maybe GetPropertyAttributes( Local context, Local key); V8_DEPRECATED("Use maybe version", Local GetOwnPropertyDescriptor(Local key)); V8_WARN_UNUSED_RESULT MaybeLocal GetOwnPropertyDescriptor( Local context, Local key); V8_DEPRECATE_SOON("Use maybe version", bool Has(Local key)); V8_WARN_UNUSED_RESULT Maybe Has(Local context, Local key); V8_DEPRECATE_SOON("Use maybe version", bool Delete(Local key)); // TODO(dcarney): mark V8_WARN_UNUSED_RESULT Maybe Delete(Local context, Local key); V8_DEPRECATED("Use maybe version", bool Has(uint32_t index)); V8_WARN_UNUSED_RESULT Maybe Has(Local context, uint32_t index); V8_DEPRECATED("Use maybe version", bool Delete(uint32_t index)); // TODO(dcarney): mark V8_WARN_UNUSED_RESULT Maybe Delete(Local context, uint32_t index); V8_DEPRECATED("Use maybe version", bool SetAccessor(Local name, AccessorGetterCallback getter, AccessorSetterCallback setter = 0, Local data = Local (), AccessControl settings = DEFAULT, PropertyAttribute attribute = None)); V8_DEPRECATED("Use maybe version", bool SetAccessor(Local name, AccessorNameGetterCallback getter, AccessorNameSetterCallback setter = 0, Local data = Local (), AccessControl settings = DEFAULT, PropertyAttribute attribute = None)); // TODO(dcarney): mark V8_WARN_UNUSED_RESULT Maybe SetAccessor(Local context, Local name, AccessorNameGetterCallback getter, AccessorNameSetterCallback setter = 0, MaybeLocal data = MaybeLocal (), AccessControl settings = DEFAULT, PropertyAttribute attribute = None); void SetAccessorProperty(Local name, Local getter, Local setter = Local (), PropertyAttribute attribute = None, AccessControl settings = DEFAULT); Maybe HasPrivate(Local context, Local key); Maybe SetPrivate(Local context, Local key, Local value); Maybe DeletePrivate(Local context, Local key); MaybeLocal GetPrivate(Local context, Local key); V8_DEPRECATE_SOON("Use maybe version", Local GetPropertyNames()); V8_WARN_UNUSED_RESULT MaybeLocal GetPropertyNames( Local context); V8_WARN_UNUSED_RESULT MaybeLocal GetPropertyNames( Local context, KeyCollectionMode mode, PropertyFilter property_filter, IndexFilter index_filter); V8_DEPRECATE_SOON("Use maybe version", Local GetOwnPropertyNames()); V8_WARN_UNUSED_RESULT MaybeLocal GetOwnPropertyNames( Local context); V8_WARN_UNUSED_RESULT MaybeLocal GetOwnPropertyNames( Local context, PropertyFilter filter); Local GetPrototype(); V8_DEPRECATED("Use maybe version", bool SetPrototype(Local prototype)); V8_WARN_UNUSED_RESULT Maybe SetPrototype(Local context, Local prototype); Local
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/81320.html
摘要:這些是中可用的最快屬性。通常來說我們將線性屬性存儲(chǔ)中存儲(chǔ)的屬性稱為。因此也支持所謂的屬性。整數(shù)索引屬性的處理和命名屬性的復(fù)雜性相同。 本文為譯文,原文地址:http://v8project.blogspot.com...,作者,@Camillo Bruni ,V8 JavaScript Engine Team Blog 在這篇博客中,我們想解釋 V8 如何在內(nèi)部處理 JavaScrip...
摘要:在運(yùn)行腳本時(shí),需要顯示的指定對(duì)象。大對(duì)象區(qū)每一個(gè)區(qū)域都是由一組內(nèi)存頁構(gòu)成的。這里是唯一擁有執(zhí)行權(quán)限的內(nèi)存區(qū)。換句話說,是該對(duì)象被之后所能回收到內(nèi)存的總和。一旦活躍對(duì)象已被移出,則在舊的半空間中剩下的任何死亡對(duì)象被丟棄。 內(nèi)存管理 本文以V8為背景 對(duì)之前的文章進(jìn)行重新編輯,內(nèi)容做了很多的調(diào)整,使其具有邏輯更加緊湊,內(nèi)容更加全面。 1. 基礎(chǔ)概念 1.1 生命周期 不管什么程序語言,內(nèi)存...
摘要:的內(nèi)存限制和垃圾回收機(jī)制內(nèi)存限制內(nèi)存限制一般的后端語言開發(fā)中,在基本的內(nèi)存使用是沒有限制的。的內(nèi)存分代目前沒有一種垃圾自動(dòng)回收算法適用于所有場景,所以的內(nèi)部采用的其實(shí)是兩種垃圾回收算法。 前言 從前端思維轉(zhuǎn)變到后端, 有一個(gè)很重要的點(diǎn)就是內(nèi)存管理。以前寫前端因?yàn)橹皇窃跒g覽器上運(yùn)行, 所以對(duì)于內(nèi)存管理一般不怎么需要上心, 但是在服務(wù)器端, 則需要斤斤計(jì)較內(nèi)存。 V8的內(nèi)存限制和垃圾回收機(jī)...
摘要:關(guān)注于運(yùn)行中的內(nèi)存信息的展示,用可視化的方式還原了,有助于理解內(nèi)存管理。背景運(yùn)行過程中的大部分?jǐn)?shù)據(jù)都保存在堆中,所以性能分析另一個(gè)比較重要的方面是內(nèi)存,也就是堆的分析。上周發(fā)布了工具,可以用來動(dòng)態(tài)地展示的結(jié)果,分析各種函數(shù)的調(diào)用關(guān)系。 OneHeap 關(guān)注于運(yùn)行中的 JavaScript 內(nèi)存信息的展示,用可視化的方式還原了 HeapGraph,有助于理解 v8 內(nèi)存管理。 ...
摘要:歡迎來我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動(dòng)及頁面渲染優(yōu)化理論寫法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...
閱讀 1084·2021-11-25 09:43
閱讀 706·2021-11-22 14:45
閱讀 3833·2021-09-30 09:48
閱讀 1072·2021-08-31 09:41
閱讀 1979·2019-08-30 13:52
閱讀 1986·2019-08-30 11:24
閱讀 1354·2019-08-30 11:07
閱讀 961·2019-08-29 12:15