Object Representation

Khi một đối tượng trong JS được tạo, V8 sẽ tạo ra một con trỏ có giá trị là vùng nhớ của đối tượng JSObject trong C++.

Bên trong một JSObject sẽ bao gồm những thành phần sau:

  • Map: con trỏ của hidden class của đối tượng.
  • Properties: con trỏ của đối tượng chứa các thuộc tính được đặt tên (named properties).
  • Elements: con trỏ của đối tượng chứa các thuộc tính được đánh chỉ số (indexed properties).
  • In-object properties: các con trỏ của các thuộc tính được lưu bên trong vùng nhớ của object.

Minh họa:

In-Object Properties

Là các thuộc tính được lưu trữ trong vùng nhớ của object.

Các thuộc tính được khai báo trong quá trình khởi tạo object (chẳng hạn như thuộc tính a trong let o = {a:1}) đều được lưu trữ trong vùng nhớ của object. Trong khi đó, các thuộc tính được thêm vào sau quá trình khởi tạo object có thể không được lưu trong vùng nhớ của object.

Xét ví dụ sau:

const obj = {}; 
obj.a = 1; 
% DebugPrint(obj);

Info

%DebugPrint là một native function của V8 giúp in ra thông tin của object.

Output khi chạy với D8 Shell:

d8> %DebugPrint(obj)
DebugPrint: 0x134a001cc789: [JS_OBJECT_TYPE]
 - map: 0x134a000db555 <Map[16](HOLEY_ELEMENTS)> [FastProperties]
 - prototype: 0x134a000c4b79 <Object map = 0x134a000c41b5>
 - elements: 0x134a00000219 <FixedArray[0]> [HOLEY_ELEMENTS]
 - properties: 0x134a00000219 <FixedArray[0]>
 - All own properties (excluding elements): {
    0x134a00002a49: [String] in ReadOnlySpace: #a: 1 (const data field 0), location: in-object <----
 }
...

Trong output trên, ta thấy rằng thuộc tính alocationin-object.

Số lượng các thuộc tính in-object sẽ có giới hạn tùy thuộc vào kích thước ban đầu của thuộc tính và sẽ được lưu ở trong vùng nhớ của con trỏ properties nếu có nhiều thuộc tính được thêm vào.

Ví dụ:

const obj = {};
obj.a = 1;
obj.b = 2;
obj.c = 3;
obj.d = 4;
obj.e = 5;
% DebugPrint(obj);

Output:

d8> %DebugPrint(obj)
DebugPrint: 0x327001cc785: [JS_OBJECT_TYPE]
 - map: 0x0327000dce45 <Map[28](HOLEY_ELEMENTS)> [FastProperties]
 - prototype: 0x0327000c4b79 <Object map = 0x327000c41b5>
 - elements: 0x032700000219 <FixedArray[0]> [HOLEY_ELEMENTS]
 - properties: 0x0327001ce8e9 <PropertyArray[3]>
 - All own properties (excluding elements): {
    0x32700002a49: [String] in ReadOnlySpace: #a: 1 (const data field 0), location: in-object
    0x32700002a59: [String] in ReadOnlySpace: #b: 2 (const data field 1), location: in-object
    0x32700002a69: [String] in ReadOnlySpace: #c: 3 (const data field 2), location: in-object
    0x32700002a79: [String] in ReadOnlySpace: #d: 4 (const data field 3), location: in-object
    0x32700002a89: [String] in ReadOnlySpace: #e: 5 (const data field 4), location: properties[0]
 }
...

Trong output trên, ta thấy thuộc tính thứ 5 được thêm vào không còn nằm trong cùng vùng nhớ với object nữa mà sẽ được lưu ở trong vùng nhớ của con trỏ properties.

Properties

Con trỏ properties của một object sẽ trỏ đến một đối tượng và bên trong đối tượng đó sẽ có một mảng lưu trữ giá trị của các named property. Để truy xuất một named property ở trong mảng của properties (mà ta gọi là backing store của properties), V8 sẽ sử dụng instance descriptor có trong hidden class.

Việc truy xuất giá trị của thuộc tínha diễn ra như sau:

  1. Truy cập đến map của object.
  2. Từ map, tìm instance descriptors.
  3. Truy xuất index của a ở trong instance descriptors.
  4. Sử dụng index có được để truy xuất giá trị của a ở trong backing store của properties.

Trong trường hợp việc thêm hoặc xóa thuộc tính ra khỏi object diễn ra quá thường xuyên, V8 sẽ chuyển cấu trúc dữ liệu lưu giá trị của các named property từ mảng sang dictionary. Khi đó, dictionary sẽ chứa tất cả các metadata của các thuộc tính và instance descriptor ở trong map của object sẽ là rỗng:

Ta gọi đối tượng được trỏ đến bởi properties mà lưu các thuộc tính ở trong mảng là FastProperties còn đối tượng được trỏ đến bởi properties mà lưu các thuộc tính trong dictionary là SlowProperties.

Elements

Đối với các indexed property, chúng cũng được lưu trong backing store của đối tượng mà elements trỏ đến và sẽ được truy xuất thông qua chỉ số.

Backing store của elements có nhiều loại tùy thuộc vào kiểu dữ liệu của các phần tử bên trong mảng. Việc xác định loại backing store (hay còn gọi là element kind) giúp V8 tối ưu việc sử dụng các thao tác trên mảng dựa trên kiểu dữ liệu của các phần tử bên trong nó.

Có 2 element kind chính là PACKEDHOLEY. Nếu element kind là PACKED, các phần tử bên trong mảng sẽ nằm sát nhau. Ngược lại với nó là HOLEY, các phần tử bên trong mảng nằm thưa với nhau và xảy ra khi chúng ta xóa hoặc không khai báo phần tử.

PACKEDHOLEY lại được chia thành 3 loại dành cho các số nguyên nhỏ (small integer - SMI), số chấm động chính xác kép (double) và các giá trị tổng quát:

Các mảng có element kind cụ thể có thể được chuyển thành mảng có element kind tổng quát nhưng không có chiều ngược lại.

Tương tự với properties, backing store của elements cũng có thể ở dạng dictionary. Điều này xảy ra khi mảng được khai báo với kích thước rất lớn nhưng chỉ có một vài phần tử.

list
from outgoing([[Object Representation]])
sort file.ctime asc

Resources