初探V8中的JS对象
跟着以下的文章中学习时的一些实操、记录。
一些关于JS对象的很好的文章
什么是JS原型链?
JS 的 new 到底是干什么的?
V8 是怎么跑起来的 —— V8 中的对象表示
奇技淫巧学V8之二,对象在V8内的表达
V8中的JS对象
定义如下a、b两个对象,在Chrome中用开发者工具看一下内存快照,也在Ubuntu虚拟机中跑一下debug版的d8,gdb中看一下内存,结合起来学习。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function Foo1 () {} var a = new Foo1(); var b = new Foo1();
a.name = 'name-a'; a.text = 'aaa'; b.name = 'name-b'; b.text = 'bbb'; a[1] = "a-1"; a[2] = "a-2";
%DebugPrint(a); %DebugPrint(b); %SystemBreak();
|
chrome开发者工具内存快照中看到的这两个对象

%DebugPrint()
打印出来的这两个对象如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| DebugPrint: 0x62a9128df09: [JS_OBJECT_TYPE] //a - map: 0x3414d4e4ac79 <Map(HOLEY_ELEMENTS)> [FastProperties] - prototype: 0x062a9128de09 <Object map = 0x3414d4e4acc9> - elements: 0x062a9128e0a1 <FixedArray[19]> [HOLEY_ELEMENTS] - properties: 0x39d537600c71 <FixedArray[0]> { #name: 0x09e99975f229 <String[#6]: name-a> (const data field 0) #text: 0x09e99975f259 <String[#3]: aaa> (const data field 1) } - elements: 0x062a9128e0a1 <FixedArray[19]> { 0: 0x39d5376005b1 <the_hole> 1: 0x09e99975f2a1 <String[#3]: a-1> 2: 0x09e99975f2b9 <String[#3]: a-2> 3-18: 0x39d5376005b1 <the_hole> } 0x3414d4e4ac79: [Map] - type: JS_OBJECT_TYPE - instance size: 104 - inobject properties: 10 - elements kind: HOLEY_ELEMENTS - unused property fields: 8 - enum length: invalid - stable_map - back pointer: 0x3414d4e4ac29 <Map(HOLEY_ELEMENTS)> - prototype_validity cell: 0x09e99975f901 <Cell value= 1> - instance descriptors (own) #2: 0x062a9128e041 <DescriptorArray[2]> - layout descriptor: (nil) - prototype: 0x062a9128de09 <Object map = 0x3414d4e4acc9> - constructor: 0x09e99975f729 <JSFunction Foo1 (sfi = 0x9e99975f401)> - dependent code: 0x39d5376002c1 <Other heap object (WEAK_FIXED_ARRAY_TYPE)> - construction counter: 5
DebugPrint: 0x62a9128df71: [JS_OBJECT_TYPE] //b - map: 0x3414d4e4ac79 <Map(HOLEY_ELEMENTS)> [FastProperties] - prototype: 0x062a9128de09 <Object map = 0x3414d4e4acc9> - elements: 0x39d537600c71 <FixedArray[0]> [HOLEY_ELEMENTS] - properties: 0x39d537600c71 <FixedArray[0]> { #name: 0x09e99975f271 <String[#6]: name-b> (const data field 0) #text: 0x09e99975f289 <String[#3]: bbb> (const data field 1) } 0x3414d4e4ac79: [Map] - type: JS_OBJECT_TYPE - instance size: 104 - inobject properties: 10 - elements kind: HOLEY_ELEMENTS - unused property fields: 8 - enum length: invalid - stable_map - back pointer: 0x3414d4e4ac29 <Map(HOLEY_ELEMENTS)> - prototype_validity cell: 0x09e99975f901 <Cell value= 1> - instance descriptors (own) #2: 0x062a9128e041 <DescriptorArray[2]> - layout descriptor: (nil) - prototype: 0x062a9128de09 <Object map = 0x3414d4e4acc9> - constructor: 0x09e99975f729 <JSFunction Foo1 (sfi = 0x9e99975f401)> - dependent code: 0x39d5376002c1 <Other heap object (WEAK_FIXED_ARRAY_TYPE)> - construction counter: 5
|
单独看一下a对象如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| pwndbg> job 0x62a9128df09 //a 0x62a9128df09: [JS_OBJECT_TYPE] - map: 0x3414d4e4ac79 <Map(HOLEY_ELEMENTS)> [FastProperties] - prototype: 0x062a9128de09 <Object map = 0x3414d4e4acc9> - elements: 0x062a9128e0a1 <FixedArray[19]> [HOLEY_ELEMENTS] - properties: 0x39d537600c71 <FixedArray[0]> { #name: 0x09e99975f229 <String[#6]: name-a> (const data field 0) #text: 0x09e99975f259 <String[#3]: aaa> (const data field 1) } - elements: 0x062a9128e0a1 <FixedArray[19]> { 0: 0x39d5376005b1 <the_hole> 1: 0x09e99975f2a1 <String[#3]: a-1> 2: 0x09e99975f2b9 <String[#3]: a-2> 3-18: 0x39d5376005b1 <the_hole> } pwndbg> telescope 0x62a9128df08 //a 00:0000│ 0x62a9128df08 —▸ 0x3414d4e4ac79 ◂— 0xd000039d5376001 //map 01:0008│ 0x62a9128df10 —▸ 0x39d537600c71 ◂— 0x39d5376008 //properties 02:0010│ 0x62a9128df18 —▸ 0x62a9128e0a1 ◂— 0x39d5376008 //elements 03:0018│ 0x62a9128df20 —▸ 0x9e99975f229 ◂— 0x96000039d5376004 //properties["name"] 04:0020│ 0x62a9128df28 —▸ 0x9e99975f259 ◂— 0xa000039d5376004 //properties["text"] 05:0028│ 0x62a9128df30 —▸ 0x39d537600321 ◂— 0x1000039d5376001 //slot ... ↓ pwndbg> telescope 0x062a9128e0a0 //elements 00:0000│ 0x62a9128e0a0 —▸ 0x39d537600801 ◂— 0x39d5376001 01:0008│ 0x62a9128e0a8 ◂— 0x1300000000 //0x13=19 02:0010│ 0x62a9128e0b0 —▸ 0x39d5376005b1 ◂— 0xff000039d5376005 03:0018│ 0x62a9128e0b8 —▸ 0x9e99975f2a1 ◂— 0xbe000039d5376004 //elements[1] 04:0020│ 0x62a9128e0c0 —▸ 0x9e99975f2b9 ◂— 0x22000039d5376004 //elements[2] 05:0028│ 0x62a9128e0c8 —▸ 0x39d5376005b1 ◂— 0xff000039d5376005 ... ↓
|
一些基本点:
可索引属性会放在elements对象中。
命名属性有三种的不同存储方式:对象内属性(in-object)、快属性(fast)和慢属性(slow),其中,快属性和慢属性是存放在properties对象中的,快属性和慢属性的区别在于存储结构。
对象内属性是预分配好空间的,当位置(slot)不足时,才会把属性放进properties对象中。所以看上面gdb中的a对象的输出可以看到,只有两个命名属性,name
和text
都以对象内属性的方式存在,紧跟在elements对象指针后面,而properties则应是空的<FixedArray[0]>
。
- 对象内属性保存在对象本身,提供最快的访问速度。
- 快属性比对象内属性多了一次寻址时间。
- 慢属性与前面的两种属性相比,会将属性的完整结构存储
From V8 是怎么跑起来的 —— V8 中的对象表示

【上图来自奇技淫巧学V8之二,对象在V8内的表达】

【上图来自V8 是怎么跑起来的 —— V8 中的对象表示】
map
map大概就是描述这个对象一些信息的一个东西…也叫隐藏类(Hidden Class)
a对象中的map:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 0x3414d4e4ac79: [Map] - type: JS_OBJECT_TYPE //实例类型 - instance size: 104 //实例大小 - inobject properties: 10 //对象内属性存储空间(包含未使用的slots) - elements kind: HOLEY_ELEMENTS - unused property fields: 8 //未使用的属性slot数 - enum length: invalid - stable_map //对象当前处于快速模式(dictionary_map:字典模式) - back pointer: 0x3414d4e4ac29 <Map(HOLEY_ELEMENTS)> - prototype_validity cell: 0x09e99975f901 <Cell value= 1> - instance descriptors (own) #2: 0x062a9128e041 <DescriptorArray[2]> //标识对象实例的属性名与其值的存取位置 - layout descriptor: (nil) - prototype: 0x062a9128de09 <Object map = 0x3414d4e4acc9> - constructor: 0x09e99975f729 <JSFunction Foo1 (sfi = 0x9e99975f401)> - dependent code: 0x39d5376002c1 <Other heap object (WEAK_FIXED_ARRAY_TYPE)> - construction counter: 5 ------------------------------------------------------------------------------- pwndbg> job 0x062a9128e041 0x62a9128e041: [DescriptorArray] - map: 0x39d537600271 <Map> - enum_cache: empty - nof slack descriptors: 0 - nof descriptors: 2 - raw marked descriptors: mc epoch 0, marked 0 [0]: #name (const data field 0:h, p: 0, attrs: [WEC]) @ Any [1]: #text (const data field 1:h, p: 1, attrs: [WEC]) @ Any
|
v8概览
V8由很多子模块构成,其中,有几个最重要的模块:
Parser:负责将JavaScript源码转换为Abstract Syntax Tree (AST)
Ignition:interpreter,即解释器,负责将AST转换为Bytecode,解释执行Bytecode;同时收集TurboFan优化编译所需的信息,比如函数参数的类型
TurboFan:compiler,即编译器,利用Ignitio所收集的类型信息,将Bytecode转换为优化的汇编代码
Orinoco:garbage collector,垃圾回收模块,负责将程序不再需要的内存空间回收
Parser、Ignition、Turbofan协作将JS源码最终编译成汇编代码