IlmHamroh
JavaScript Full-stack/24-qism. Node.js9/55-dars17 daqiqa
Mundarija (28)

Node.js xotira limitlari: --max-old-space-size va heap statistikasi

Qisqacha: Node'dagi har JS obyekt V8 heap'ida yashaydi va heap'ning chegarasi bor. 16 GB RAM'li kompyuterda Node 24 sukut bo'yicha ~4,2 GB beradi. O'lchandi: 4 288 MB. --max-old-space-size=256 uni o'zgartiradi — lekin natija 256 emas, 448 MB: unga young generation uchun 192 MB qo'shiladi (o'lchandi). Chegaraga yetganda FATAL ERROR: … JavaScript heap out of memory bilan jarayon o'ladi, exit kod 134 — try/catch buni ushlamaydi. Kuzatish uchun: process.memoryUsage() (rss, heapUsed, external) va v8.getHeapStatistics(). Buffer ma'lumoti heap'dan tashqarida — uning hajmi bu limitga kirmaydi.

Bu darsda

  • Heap qanday bo'limlarga bo'linishini (young, old, large object) va GC ularni qanday tozalashini bilasiz.
  • Sukut heap limiti qanday hisoblanishini va --max-old-space-size aslida nimani o'zgartirishini o'lchov bilan tushuntira olasiz.
  • process.memoryUsage() dagi rss, heapTotal, heapUsed, external, arrayBuffers ni o'qiy olasiz.
  • "heap out of memory" xatosini taniysiz va uni qanday oldini olishni bilasiz.
  • --trace-gc chiqishidan Scavenge va Mark-Compact ni ajratasiz.

Oldin bilishingiz kerak: Xotira boshqaruvi va garbage collection, Hidden classes va inline caching, process obyekti.

1. Nega bu kerak?

Kichik VPS — 1 GB RAM. Unda Node server va ma'lumotlar bazasi ishlaydi. Bir kuni server hisobot yasash paytida yiqildi, logda:

text
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

Tarjimasi: "Heap chegarasiga yetildi, joy ajratib bo'lmadi — JavaScript heap'ida xotira tugadi". Ikki savol tug'iladi. Birinchisi: chegara qancha edi va uni kim qo'ygan? Ikkinchisi: try/catch nega ushlamadi?

Teskari holat ham bo'ladi. 32 GB'li serverda dastur 3 GB dan oshganda yiqiladi, garchi 25 GB bo'sh tursa ham. Sababi bir xil — V8 heap chegarasi kompyuterning RAM'iga teng emas.

Xotira boshqaruvi va GC darsida g'oyani ko'rgansiz: obyektlar heap'da, unga yetib boradigan havola qolmasa — GC tozalaydi. Bu dars Node'ning amaliy tomoni: chegara qancha, uni qanday o'zgartirish va o'lchash.

O'xshatish: omborxona. Do'kon egasi (V8) omborga chegara qo'ygan — "ko'pi bilan 4 ta xona". Tovar ko'paysa, omborchi (GC) eski, kerakmas qutilarni chiqarib tashlaydi. Lekin hamma quti kerakli bo'lsa va 5-xona kerak bo'lsa — ish to'xtaydi, binoda bo'sh xona bo'lsa ham.

2. Heap ichida nima bor

2.1 Stack va heap — qisqa eslatma

J-173 darsidan: stack (stek) — funksiya chaqiruvlari va kichik oddiy qiymatlar, avtomatik tozalanadi. Heap (uyum) — obyektlar, massivlar, satrlar, funksiyalar; ularni GC tozalaydi. Stack ham cheklangan — juda chuqur rekursiya uni to'ldiradi:

js
let depth = 0;
function dive() {
  depth++;
  dive();
}
try {
  dive();
} catch (e) {
  console.log(`${e.name}: ${e.message}`, depth);
}

Biz ishga tushirdik (Node 24.21.0, Windows):

text
RangeError: Maximum call stack size exceeded 12487

Stack to'lishi — oddiy RangeError, uni try/catch ushlaydi. Heap to'lishi esa boshqacha — pastda ko'rasiz.

2.2 Avlodlar: young va old

V8 obyektlarni "yoshi" bo'yicha ikki avlodga ajratadi. Bu g'oya generational GC (avlodli yig'ish) deyiladi va bitta kuzatuvga asoslanadi: obyektlarning ko'pchiligi tez o'ladi. Funksiya ichidagi vaqtinchalik massiv, map natijasi, satr birlashtirish — millisekundlarda keraksiz bo'ladi.

Bo'lim Nima Qanday tozalanadi
Young generation (new_space) yangi obyektlar; kichik (o'nlab MB) Scavenge — tez, tez-tez
Old generation (old_space) ikki Scavenge'dan omon qolganlar Mark-Sweep-Compact — sekinroq, kamroq
Large object space katta obyektlar (masalan uzun massiv) old bilan birga
Code space JIT kompilyatsiya qilingan kod —

Diagrammada obyekt hayotini kuzating:

flowchart LR
  N["yangi obyekt"] --> Y["young generation<br/>(semi-space)"]
  Y -->|"Scavenge: o'lik"| X["tozalandi"]
  Y -->|"2 marta omon qoldi"| O["old generation"]
  O -->|"Mark-Sweep-Compact"| X

Scavenge — young generation'ni tozalash. Young ikki teng yarimga (semi-space) bo'lingan. Tirik obyektlar bir yarimdan ikkinchisiga ko'chiriladi, qolgani butunlay tashlanadi. Ish faqat tiriklar soniga bog'liq — o'liklar ko'p bo'lsa ham tez.

Mark-Sweep-Compact — old generation uchun uch qadam: tirik obyektlarni belgilash (mark), o'liklarni bo'shatish (sweep), bo'laklarni bir joyga zichlash (compact). Bu og'irroq, ba'zi qismlari JS'ni to'xtatadi — "GC pauzasi".

2.3 Node 24 dagi bo'limlar ro'yxati

v8.getHeapSpaceStatistics() har bo'lim haqida ma'lumot beradi. Nomlari V8 versiyasiga bog'liq — bu blok Node 24 (V8 13.6) da tekshirilgan:

js
import v8 from "node:v8";

const names = v8.getHeapSpaceStatistics().map((s) => s.space_name);
console.log(names.length);
console.log(names.slice(0, 4).join(", "));

Konsolda:

text
13
read_only_space, new_space, old_space, code_space

13 ta bo'lim: yuqoridagi jadvaldagilar va V8'ning ichki xavfsizlik va ulashilgan bo'limlari (trusted_space, shared_space …). Kundalik ishda new_space, old_space va large_object_space yetarli.

Tekshirib ko'ring: Server har so'rovda 50 KB lik vaqtinchalik obyekt yaratadi va javobdan keyin uni unutadi. Bu obyektlar qaysi bo'limda o'ladi va qaysi GC ularni tozalaydi?

Javob

Young generation'da, Scavenge bilan. Ular ikki Scavenge'dan omon qolmaydi — old generation'ga yetib bormaydi. Generational GC aynan shunday "tez o'ladigan" obyektlar uchun qurilgan, shuning uchun bunday ish arzon.

3. Heap limiti: qancha va nega

3.1 O'lchov: sukut va bayroqlar

Heap chegarasini v8.getHeapStatistics().heap_size_limit ko'rsatadi. Kanon tajribasi (16 GB RAM, Node 24.21.0) va bizning qo'shimcha o'lchovimiz:

bash
node -p "require('v8').getHeapStatistics().heap_size_limit / 2**20"
node --max-old-space-size=256 -p "..."   # xuddi shu ifoda
Bayroq heap_size_limit
(sukut) 4 288 MB
--max-old-space-size=256 448 MB
--max-old-space-size=8192 8 384 MB
--max-semi-space-size=16 4 144 MB
--max-old-space-size=256 --max-semi-space-size=16 304 MB
--max-old-space-size-percentage=10 1 797 MB

Grafikda ko'ring — qaysi bayroq qanchalik ta'sir qiladi:

Node 24 heap limiti (16 GB RAM)
  • sukut4 288 MB
  • old=256256 + 192448 MB
  • old=256, semi=16256 + 48304 MB
  • old 10%1 605 + 1921 797 MB
  • old=81928 192 + 1928 384 MB

Manba: O'lchandi: IlmHamroh kanoni (sukut, 256, 8192) va yozuvchi (semi, percentage); Node 24.21.0, Windows 11, 16 GB RAM, 2026-10-23

3.2 Natijani o'qiymiz

Ikki qonuniyat ko'rinadi.

Birinchisi: "old" faqat bir qismi. --max-old-space-size — nomidan ko'rinib turganidek, faqat old generation chegarasi. Umumiy limitga young generation ham qo'shiladi. Har o'lchovda farq bir xil — 192 MB. --max-semi-space-size=16 bilan esa farq 48 MB bo'ldi: 192 = 3 × 64, 48 = 3 × 16. Ya'ni young uchun semi-space hajmining uch baravari ajratiladi (ikki semi-space va katta yangi obyektlar uchun joy — bu V8 ichki hisobi, hujjatda tafsiloti yo'q; biz faqat o'lchovdan xulosa qildik).

Amaliy saboq: konteynerga 512 MB berib, Node'ga --max-old-space-size=512 yozsangiz, heap 704 MB gacha o'sishi mumkin. Unga kod, stack va Buffer ham qo'shiladi. OS jarayonni xotira tugadi deb o'ldiradi. Qoida: old-space chegarasini ajratilgan xotiradan ancha kam qo'ying (odatda 70–75 foiz atrofida — bu jamoalar orasidagi amaliy maslahat, qat'iy qoida emas).

Ikkinchisi: sukut RAM'ga bog'liq. 16 GB'da sukut old-space 4 096 MB (4 288 − 192). V8 old-space chegarasini fizik xotiradan hisoblaydi va 64-bitli tizimlarda yuqori chegara qo'yadi. Shuning uchun 32 GB'li serverda ham sukut taxminan shunday qoladi — katta ma'lumot bilan ishlaydigan dasturga bayroq kerak. Kichik xotirali mashinada esa sukut kamroq. Aniq formula V8 versiyasiga bog'liq va hujjatlashtirilmagan — har mashinada o'lchang.

3.3 Foiz bilan: --max-old-space-size-percentage

Node 24 ning yangi versiyalarida foiz bilan berish mumkin: --max-old-space-size-percentage=10 — "fizik xotiraning 10 foizi". O'lchovda: 16 052 MB × 0,10 ≈ 1 605 + 192 = 1 797 MB. Bu konteynerlar uchun qulay: xotira o'zgarsa, bayroqni qayta yozish kerak emas. Eski Node versiyalarida bu bayroq yo'q — node --help bilan tekshiring.

3.4 Bayroqni berish yo'llari

Usul Yozilishi
Buyruqda node --max-old-space-size=2048 server.js
Muhit o'zgaruvchisi NODE_OPTIONS="--max-old-space-size=2048"
npm skriptida "start": "node --max-old-space-size=2048 server.js"

NODE_OPTIONS qulay: Node'ni bevosita ishga tushirmaydigan vositalarga (masalan next build, vite build) ham ta'sir qiladi. Katta loyihada build "heap out of memory" bilan yiqilsa, birinchi qilinadigan ish — shu.

Tekshirib ko'ring: --max-old-space-size=1024 bilan ishga tushgan Node'da heap_size_limit taxminan qancha bo'ladi?

Javob

Taxminan 1 216 MB: 1 024 (old) + 192 (young, sukut semi-space bilan). Aynan raqam V8 versiyasiga bog'liq, shuning uchun node --max-old-space-size=1024 -p "require('v8').getHeapStatistics().heap_size_limit / 2**20" bilan tekshiring.

4. Chegaraga yetganda

4.1 Tajriba: 64 MB va cheksiz massiv

Kanon tajribasi: heap'ni ataylab kichik qilib (64 MB), cheksiz massivga obyekt qo'shamiz va hammasini try/catch ga o'raymiz. Biz ishga tushirdik (Git Bash). Telefon uchun buyruq uch qatorga bo'lingan: qator oxiridagi \ uni davom ettiradi. PowerShell'da hammasini bitta qatorga yozing.

bash
node --max-old-space-size=64 -e "try { const a = []; \
  for (;;) a.push({ x: Math.random(), y: 's'.repeat(50) }) \
} catch (e) { console.log('ushlandi', e.message) }"

Chiqishda ikki narsaga qarang: "ushlandi" bormi va oxirgi Mark-Compact qatori qancha MB ni bo'shatdi:

text
<--- Last few GCs --->

[8180:000001A807CB2000]      393 ms: Mark-Compact 64.0 97.2-bob -> 58.5 127.0-bob MB, pooled: 0 MB, 147.46 / 0.00 ms  (average mu = 0.489, current mu = 0.339) allocation failure; scavenge might not succeed
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----

echo $? → 134. "ushlandi" chiqmadi. Nega try/catch ishlamadi?

Heap to'lishi — JS istisnosi emas. V8 ichida obyekt uchun joy topilmaganda, u avval to'liq GC qiladi. Baribir joy bo'lmasa, JS'ga qaytish imkoniyati yo'q: catch blokini bajarish uchun ham xotira kerak. Shuning uchun V8 butun jarayonni abort bilan to'xtatadi. 134 = 128 + 6, SIGABRT signali (process obyekti darsidagi 128 + N kelishuvi).

Last few GCs qismi foydali: oxirgi Mark-Compact 64 MB dan atigi 58,5 MB ga tushirgan. Ya'ni deyarli hamma obyekt tirik edi, tozalaydigan narsa qolmagan. Bu "xotira sizishi" yoki "haqiqatan katta ma'lumot" belgisi.

4.2 Oldini olish

  1. Hammasini xotiraga yuklamang. 1 GB CSV'ni readFile bilan emas, oqim bilan bo'lak-bo'lak o'qing (Stream nima). Kanon o'lchovi: 1 mln qatorli CSV — oqim bilan 63 MB, butun fayl bilan 194 MB.
  2. Sizishlarni toping. O'sib boradigan global massiv, unutilgan listener, cheksiz kesh — heap asta to'ladi. Bularni Memory leak'ni topish: heap snapshot darsida ko'rasiz.
  3. Chegarani ongli qo'ying — konteyner xotirasidan kam, foiz bayrog'i bilan.
  4. Kuzating — heapUsed vaqt o'tishi bilan to'xtovsiz o'ssa, bu sizish belgisi.

5. process.memoryUsage() ni o'qish

5.1 Besh maydon

js
const keys = Object.keys(process.memoryUsage());
console.log(keys.join(", "));

Konsolda:

text
rss, heapTotal, heapUsed, external, arrayBuffers
Maydon Ma'nosi
rss Resident Set Size — jarayon RAM'da egallagan hamma joy (heap, kod, stack, Buffer…)
heapTotal V8 heap uchun hozir ajratilgan joy
heapUsed heap'ning haqiqatan band qismi (tirik + hali tozalanmagan obyektlar)
external V8 obyektlariga bog'langan, lekin heap'dan tashqaridagi C++ xotira
arrayBuffers ArrayBuffer va Buffer ma'lumoti (external ning bir qismi)

Kanon o'lchovi — bo'sh skript: rss 37 MB, heapTotal 7 MB, heapUsed 5 MB. Ya'ni Node'ning o'zi RAM'da ~37 MB oladi, JS obyektlari esa atigi 5 MB.

5.2 Bir million obyekt qancha joy oladi

js
const mb = (b) => `${(b / 2 ** 20).toFixed(1)} MB`;
const before = process.memoryUsage();
const rates = Array.from({ length: 1_000_000 }, (_, i) => ({
  kod: "USD",
  kurs: 11903.15 + i,
}));
const after = process.memoryUsage();
console.log(
  `heapUsed: ${mb(before.heapUsed)} -> ${mb(after.heapUsed)}`,
);
console.log(`rss: ${mb(before.rss)} -> ${mb(after.rss)}`);
console.log(rates.length);

Biz ishga tushirdik:

text
heapUsed: 4.1 MB -> 65.2 MB
rss: 40.4 MB -> 119.3 MB
1000000

61 MB ÷ 1 000 000 ≈ 64 bayt bitta kichik obyektga (obyekt sarlavhasi, ikkita maydon, kasr son uchun alohida "quti" va massivdagi havola). JSON matni sifatida esa bitta yozuv ~30 bayt. Xotiradagi obyekt matndan ikki baravar og'ir — katta ma'lumotda buni hisobga oling. Bir xil shakldagi obyektlar bitta "yashirin klass"ni bo'lishadi (Hidden classes), shuning uchun shakl o'zgarmasa, ular ixchamroq.

5.3 Buffer heap'dan tashqarida

Muhim nozik joy: Buffer ma'lumoti heap limitiga kirmaydi. Biz ishga tushirdik — heap 64 MB, Buffer esa 500 MB:

js
const b = Buffer.alloc(500 * 2 ** 20, 1); // 500 MB
const m = process.memoryUsage();
const mb = (x) => (x / 2 ** 20).toFixed(0);
console.log(
  `Buffer ${b.length / 2 ** 20} MB | heapUsed ${mb(m.heapUsed)} MB`,
);
console.log(
  `arrayBuffers ${mb(m.arrayBuffers)} MB | rss ${mb(m.rss)} MB`,
);

node --max-old-space-size=64 buffer.mjs — biz ishga tushirdik:

text
Buffer 500 MB | heapUsed 4 MB
arrayBuffers 500 MB | rss 531 MB

Jarayon yiqilmadi: heapUsed 4 MB, ma'lumot esa arrayBuffers da. rss esa 531 MB — RAM'ni haqiqatan oldi. Xulosa: heap limiti "jarayon ko'pi bilan shuncha RAM oladi" degani emas. Konteyner xotirasini rejalashtirishda rss ga qarang. Buffer ni Buffer va binar ma'lumot darsida o'rganasiz.

6. --trace-gc — GC'ni ko'z bilan ko'rish

--trace-gc bayrog'i har GC'ni bitta qator bilan chiqaradi. 300 000 ta obyekt yaratadigan skript — biz ishga tushirdik (dastlabki qatorlar):

js
const keep = [];
for (let i = 0; i < 300_000; i++) {
  keep.push({ kod: "USD", kurs: 11903.15 + i });
}
console.log(keep.length);

node --trace-gc gc.mjs — har qatorda -> dan oldingi va keyingi sonlarni solishtiring. Qavs ichidagi sonlar (masalan (28.3)) bo'lim raqami emas, heap uchun ajratilgan joy (MB):

text
[21244:0000028096F91000]       75 ms: Scavenge 4.9 6.3-bob -> 4.4 7.3-bob MB, pooled: 0 MB, 3.32 / 0.00 ms  (average mu = 1.000, current mu = 1.000) allocation failure;
[21244:0000028096F91000]       90 ms: Scavenge 7.2 10.4-bob -> 6.6 14.2-bob MB, pooled: 0 MB, 6.71 / 0.00 ms  (average mu = 1.000, current mu = 1.000) allocation failure;
[21244:0000028096F91000]      139 ms: Scavenge 18.8 28.3-bob -> 17.4 45.5-bob MB, pooled: 0 MB, 24.63 / 0.00 ms  (average mu = 1.000, current mu = 1.000) allocation failure;

Bitta qatorni bo'laklab o'qiymiz:

Qism Ma'nosi
[21244:…] PID va ichki identifikator
139 ms jarayon boshidan o'tgan vaqt
Scavenge GC turi (young); Mark-Compact — old
18.8 28.3-bob -> 17.4 45.5-bob MB band (ajratilgan) oldin → keyin
24.63 / 0.00 ms GC davomiyligi
allocation failure sabab: yangi obyektga joy qolmadi

Bu yerda Scavenge deyarli hech narsani tozalamadi (18,8 → 17,4): hamma obyekt keep massivida tirik. Ular old generation'ga ko'chadi va heap o'sadi. Haqiqiy vaziyatda bunday qatorlar ko'p bo'lsa va Mark-Compact ko'p vaqt olsa — dastur vaqtining katta qismi GC'ga ketyapti.

7. CLI qadami: holat yo'q

valyuta ga bu darsdan kod qo'shilmaydi — o'lchov shuni ko'rsatdi:

  • CLI'ning eng katta ma'lumoti — 74 ta kurs (25,5 KB JSON). Bir million obyekt ~61 MB bo'lsa, 74 tasi — kilobaytlar.
  • Bo'sh Node 37 MB rss oladi; valyuta uning ustiga deyarli hech narsa qo'shmaydi. Sukut 4 GB limitga yaqinlashish imkoni yo'q.

Lekin keyinroq valyuta 1 million qatorli CSV yasaydi (Amaliyot: katta faylni stream bilan). U yerda bu darsdagi memoryUsage() bilan o'lchab, butun faylni xotiraga yig'ish (194 MB) va oqim (63 MB) farqini o'z ko'zingiz bilan ko'rasiz.

8. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
JavaScript heap out of memory, exit 134 heap chegarasi to'ldi ma'lumotni oqim bilan; sizishni toping; kerak bo'lsa limitni oshiring
try/catch OOM'ni ushlamadi OOM — JS istisnosi emas, abort xotirani oldindan nazorat qiling
Konteyner o'ldirildi, Node xato yozmadi heap + Buffer + kod > konteyner xotirasi old-space'ni konteynerdan kam qo'ying, rss ni kuzating
--max-old-space-size=512, lekin heap 704 MB young generation qo'shiladi heap_size_limit ni o'lchang
next build OOM bilan yiqildi build vositasi ham Node NODE_OPTIONS=--max-old-space-size=…
RangeError: Maximum call stack size exceeded cheksiz/juda chuqur rekursiya sikl yoki to'xtash sharti; bu heap emas, stack

9. Mashqlar

Mashqlar kurs/mashqlar/24/09-xotira/ da.

1-mashq (oson): qaysi maydon?

  • Butun jarayon RAM'da qancha joy olgani —
  • JS obyektlari haqiqatan egallagan heap —
  • Buffer.alloc(10_000_000) ma'lumoti qayerda hisoblanadi —
  • Yangi obyektlar yashaydigan avlod — generation
  • Old generation'ni tozalaydigan GC — Mark-Sweep-
Yechim
  • rss — heap, kod, stack, Buffer — hammasi.
  • heapUsed — heapTotal dan kichik; farqi — ajratilgan, lekin bo'sh joy.
  • arrayBuffers — u external ichida ham hisoblanadi; heap limitiga kirmaydi.
  • young — Scavenge tozalaydi.
  • Compact — belgilash, tozalash, zichlash.

2-mashq (o'rta): limitni oldindan hisoblang

O'lchovdan chiqqan qonuniyat: heap_size_limit ≈ old + 3 × semi (MB). Sukut semi — 64 MB. predictLimit(oldMb, semiMb = 64) funksiyasini yozing va jadvaldagi uchta o'lchov bilan solishtiring: (256) → 448, (8192) → 8384, (256, 16) → 304. Har qator uchun mos yoki mos emas chiqaring.

Yechim
js
function predictLimit(oldMb, semiMb = 64) {
  return oldMb + 3 * semiMb;
}

const measured = [
  { args: [256], limit: 448 },
  { args: [8192], limit: 8384 },
  { args: [256, 16], limit: 304 },
];
for (const m of measured) {
  const p = predictLimit(...m.args);
  const ok = p === m.limit ? "mos" : "mos emas";
  console.log(`${m.args.join(", ")}: ${p} — ${ok}`);
}

Konsolda:

text
256: 448 — mos
8192: 8384 — mos
256, 16: 304 — mos

Uchala o'lchov formulaga mos. Lekin bu — bitta mashina va bitta V8 versiyasidagi kuzatuv, kafolatlangan qoida emas. V8 yangilanganda o'zgarishi mumkin, shuning uchun production'da baribir heap_size_limit ni o'lchang.

3-mashq (qiyin): xotira hisoboti

Node skripti yozing: report(label) — process.memoryUsage() dan heapUsed va arrayBuffers ni MB'da (bir xona kasr bilan) va v8.getHeapStatistics() dan limitni (butun MB) bitta qatorda chiqarsin. Uni uch marta chaqiring: boshida, 200 000 obyekt yaratgandan keyin va 50 MB Buffer yaratgandan keyin. Natijada arrayBuffers faqat oxirgi qadamda o'sganini, heapUsed esa ikkinchi qadamda o'sganini tekshiring — buni true/false bilan chiqaring.

Yechim
js
import v8 from "node:v8";

const toMb = (b) => Math.round((b / 2 ** 20) * 10) / 10;
function report(label) {
  const m = process.memoryUsage();
  const { heap_size_limit } = v8.getHeapStatistics();
  const limit = Math.round(heap_size_limit / 2 ** 20);
  const heap = toMb(m.heapUsed);
  return { label, heap, buffers: toMb(m.arrayBuffers), limit };
}

const start = report("boshida");
const objects = Array.from({ length: 200_000 }, (_, i) => ({ i }));
const afterObjects = report("obyektlar");
const buf = Buffer.alloc(50 * 2 ** 20);
const afterBuffer = report("buffer");

console.log(afterObjects.heap - start.heap > 3);
console.log(afterObjects.buffers - start.buffers < 1);
console.log(afterBuffer.buffers - afterObjects.buffers >= 50);
console.log(objects.length, buf.length / 2 ** 20);

Konsolda:

text
true
true
true
200000 50

Raqamlarning o'zi (masalan heap: 9.8) har kompyuterda boshqa, shuning uchun mashq farqlarni tekshiradi. 200 000 obyekt heap'ni bir necha MB oshiradi, arrayBuffers ga tegmaydi. 50 MB Buffer esa aksincha. Oxirgi qator obyektlar va buffer'ni "tirik" ushlab turadi — aks holda GC ularni o'lchovdan oldin tozalashi mumkin edi.

10. Real ishda

  • Konteynerlar — Docker va Kubernetes'da xotira chegarasi bor; Node heap'i va rss uni hisobga olishi kerak. Node sukut limitida konteyner (cgroup) chegarasini qanchalik hisobga olishi versiyaga bog'liq — biz Linux konteynerda sinamadik. Shuning uchun bayroqni (yoki foiz bayrog'ini) aniq qo'yish ishonchliroq.
  • Build vositalari — katta TypeScript yoki Next.js loyihada tsc va next build OOM bilan yiqilishi odatiy hol; yechim — NODE_OPTIONS=--max-old-space-size=4096 va build'ni bo'lish.
  • Monitoring — production'da heapUsed va rss grafigi kuzatiladi. "Arra tishi" (o'sib, GC'da tushadi) — normal. To'xtovsiz o'sish — sizish (Memory leak'ni topish).
  • Intervyu savoli: "Node'da heap out of memory'ni qanday hal qilasiz?" — avval sababini aniqlash (katta ma'lumot yoki sizish), keyin oqim/paginatsiya, oxirida limitni oshirish.

Xulosa

  • V8 heap'i avlodlarga bo'lingan: young (Scavenge, tez) va old (Mark-Sweep-Compact, og'irroq).
  • Sukut limit RAM'ga bog'liq: 16 GB'da 4 288 MB; --max-old-space-size faqat old qismini beradi, young (192 MB) ustiga qo'shiladi.
  • Chegaraga yetganda — FATAL ERROR: … heap out of memory, exit 134; try/catch ushlamaydi.
  • memoryUsage(): rss — butun jarayon, heapUsed — JS obyektlari, arrayBuffers — Buffer (heap limitidan tashqarida).
  • --trace-gc har GC'ni ko'rsatadi; ko'p va uzoq Mark-Compact — muammo belgisi.

Keyingi dars: CommonJS ichidan: module wrapper, resolution va circular require — require faylni qanday topadi, nimaga o'raydi va sikl bo'lsa nima beradi.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Node.js xotira limitlari: --max-old-space-size va heap statistikasi — IlmHamroh