Object ichki tuzilishi
V8 obyektlarni qanday saqlaydi — Hidden Class, offset, Transition Chain, Inline Cache va Dictionary Mode ga o'tish.
22-dars
Object ichki tuzilishi
1. Nega bu mavzu muhim?
{} yozganingizda "oddiy obyekt yaratdim" deb o'ylaysiz. Lekin V8 buni hash-map sifatida saqlamaydi (garchi JS spetsifikatsiyasida object "key-value collection" deb ta'riflansa ham). Agar shunday bo'lganida, har bir obj.name chaqiruvi hash hisoblab, keyin qidiruv qilardi — bu millionlab marta chaqirilganda juda sekin bo'lardi.
V8 buning o'rniga Hidden Class (ichki nomi — Map, boshqa enginelarda "Shape" yoki "Structure") degan mexanizm orqali obyektlarni statik tildagi struct kabi ishlaydi.
2. Obyekt ikki qismga bo'linadi
const user = { name: 'Ali', age: 25 };
V8 buni shunday saqlaydi:
A) Hidden Class (metadata) — qanday property lar bor va ular qaysi offsetda:
HiddenClass_C2: { name → offset 0, age → offset 1 }
B) Property Storage (real qiymatlar) — massiv kabi ketma-ket xotira:
user:
properties[0] = 'Ali'
properties[1] = 25
hiddenClassPointer → C2
user.name chaqirilganda V8 hash hisoblamaydi — Hidden Class dan name ning offseti 0 ekanini bilib, to'g'ridan-to'g'ri properties[0] ga boradi. Bu O(1), pointer arifmetikasi darajasida tez — massiv indexiga murojaat qilish kabi.
Offset degani nima
Tasavvur qiling:
const user = {
name: "Ali",
age: 20,
city: "Toshkent"
}
Hidden Class shunday deydi:
name→ offset 0age→ offset 1city→ offset 2
Shuning uchun user.age qidirib o'tirmaydi — birdaniga age = offset 1 deb oladi. Offset — property qaysi indexda joylashganini bildiruvchi raqam, xolos.
3. Transition Chain — Hidden Class qanday quriladi
const obj = {}; // C0: {}
obj.x = 1; // C0 → C1: {x}
obj.y = 2; // C1 → C2: {x, y}
Har bir yangi property qo'shilganda V8 yangi Hidden Class yasaydi va oldingisidan transition qiladi:
add "x" add "y"
C0 ───────────→ C1 ───────────→ C2
{} {x} {x, y}
Bu daraxt shaklida — bitta Hidden Class dan bir nechta yo'nalishda transition bo'lishi mumkin:
C1 {x}
/ \
add "y" add "z"
C2 {x,y} C3 {x,z}
Muhim natija: agar ikkita obyekt bir xil tartibda bir xil property qo'shsa, ular bitta Hidden Classni bo'lishadi:
function makePoint(x, y) {
const p = {};
p.x = x;
p.y = y;
return p;
}
const p1 = makePoint(1, 2); // C2 {x, y}
const p2 = makePoint(3, 4); // xuddi shu C2 {x, y}
Bu V8 xotirasini tejaydi (Hidden Class faqat bir marta yaratiladi) va Inline Cache (IC) optimallashtirishga imkon beradi — chunki JIT compiler "bu funksiya doim C2 shaklidagi obyektlar bilan ishlaydi" deb bilib, tezkor mashina kodi generatsiya qiladi.
4. Constructor orqali — eng barqaror yo'l
function Point(x, y) {
this.x = x;
this.y = y;
}
const p1 = new Point(1, 2);
const p2 = new Point(3, 4);
Bu yerda this.x va this.y har doim bir xil tartibda qo'shiladi (constructor kodi bir xil bo'lgani uchun) — shu sababli barcha Point instance lari bitta Hidden Classga ega bo'ladi. Bu classlarning (va constructor function larning) tezligi sirlaridan biri.
Solishtiring — tartib buzilsa:
function A() { this.x = 1; this.y = 2; }
function B() { this.y = 1; this.x = 2; } // teskari tartib!
const a = new A(); // Hidden Class: {x, y}
const b = new B(); // Hidden Class: {y, x} — BOSHQA!
Property nomlari bir xil, lekin qo'shilish tartibi farqli — shuning uchun a va b turli Hidden Classlarga ega. Agar bitta funksiya ba'zan A tipidagi, ba'zan B tipidagi obyektlar bilan ishlasa, bu funksiya polymorphic (yoki megamorphic) bo'lib qoladi — V8 Inline Cache buzilib, sekinlashadi.
5. Inline Cache (IC) — bu nima va nega muhim?
function getX(obj) {
return obj.x;
}
Bu funksiya birinchi marta chaqirilganda V8 "sekin yo'l" bilan obj.x ni topadi va shu obyektning Hidden Classini eslab qoladi:
IC state: "Monomorphic" — faqat C2 shaklidagi obyektlar kutilmoqda, offset 0
Keyingi chaqiruvlarda, agar kelgan obyekt xuddi shu Hidden Class (C2) bo'lsa, V8 hech qanday qidiruv qilmasdan, to'g'ridan-to'g'ri offset 0 dan o'qiydi — bu deyarli C++ struct field access tezligida.
Agar funksiyaga har xil shakldagi obyektlar kelsa:
getX({ x: 1 }); // C_a
getX({ x: 1, y: 2 }); // C_b — boshqa shape
getX({ y: 1, x: 2 }); // C_c — yana boshqa
IC holati bosqichma-bosqich yomonlashadi:
Monomorphic (1 shape) → Polymorphic (2-4 shape) → Megamorphic (4+ shape)
Megamorphic bo'lganda V8 optimallashtirishdan butunlay voz kechadi va sekin, umumiy qidiruv yo'liga qaytadi. Shuning uchun — bir xil funksiyaga doim bir xil "shakldagi" obyektlar berish (consistent object shape) performance uchun juda muhim.
6. Dictionary Mode (Slow Mode) — Hidden Class dan voz kechish
Ba'zi holatlarda V8 Hidden Class mexanizmidan butunlay voz kechadi va obyektni haqiqiy hash-map (dictionary) rejimiga o'tkazadi:
const obj = { a: 1, b: 2, c: 3 };
delete obj.b; // ⚠️ Dictionary mode ga o'tish trigger bo'lishi mumkin
Nega delete xavfli
Transition Chain faqat qo'shishga mo'ljallangan — daraxt strukturasi. Agar o'rtadan property o'chirilsa, V8 uchun yangi, mos Hidden Class qidirish murakkablashadi.
Tasavvur qiling:
const obj = {};
obj.a = 1;
obj.b = 2;
obj.c = 3;
// Hidden Class: { a, b, c }
delete obj.b;
// Endi property lar: { a, c }
// Bu optimizatsiyani buzadi
Shu sababli ko'p property o'chirilsa yoki juda ko'p (minglab) dinamik property qo'shilsa, V8 obyektni Dictionary Mode ga o'tkazadi — "endi Hidden Class bilan ovora bo'lmayman" deb, property lar endi offsetlar orqali emas, haqiqiy hash-table orqali saqlanadi.
Dictionary mode:
- ✅ Moslashuvchan — istalgan tartibda qo'shish/o'chirish oson
- ❌ Sekinroq — har bir access hash hisoblashni talab qiladi
- ❌ Inline Cache ishlamaydi
Amaliy maslahat: agar obyektingiz "struct" kabi (fixed shape, masalan {x, y, z}) bo'lsa — Hidden Class rejimida qoldiring. Agar chindan ham dinamik key-value store kerak bo'lsa (masalan runtime da noma'lum keylar) — Map obyektidan foydalaning, oddiy {} emas, chunki Map aynan shu maqsad uchun optimallashtirilgan.
7. Hash Table — Dictionary Mode qanday qidiradi
Hash Table degani oddiy lug'at. Masalan obj.age:
age
↓
hash()
↓
123456
↓
20
Offset ishlatmaydi — hash orqali topadi. Shuning uchun biroz sekinroq.
8. Xotira tuzilishi — vizual umumlashtirish
┌─────────────────────┐
│ user (object) │
│ ┌────────────────┐ │
│ │ HiddenClass ptr │──┼──→ HiddenClass_C2 { name:0, age:1 }
│ ├────────────────┤ │
│ │ properties[0] │──┼──→ 'Ali'
│ ├────────────────┤ │
│ │ properties[1] │──┼──→ 25
│ ├────────────────┤ │
│ │ __proto__ ptr │──┼──→ Object.prototype
│ └────────────────┘ │
└─────────────────────┘
Hammasi bitta rasmda:
Object yaratildi
↓
C0
↓
x qo'shildi
↓
C1
↓
y qo'shildi
↓
C2
↓
delete y
↓
Dictionary Mode
↓
Hash Table