Chrome V8 学习笔记(一)

初探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); //d8中用来打印object内存信息
%DebugPrint(b); //d8中用来打印object内存信息
%SystemBreak(); //d8中用来中断,调试

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对象的输出可以看到,只有两个命名属性,nametext都以对象内属性的方式存在,紧跟在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源码最终编译成汇编代码

    img

0%