Zrychlení uvolňování paměti ArrayBuffer v JavaScriptu
Garbage collector v JavaScriptu spravuje paměť automaticky, ale při nepřetržitém zpracování velkých ArrayBuffer (od několika megabajtů za sekundu) dochází k prodlevám. V prohlížečích jako Chrome narůstá spotřeba paměti až na stovky megabajtů, zejména při streamovém zpracování videa. Firefox si vede lépe, ale i tam se objevují špičky. To je kritické pro 32bitové procesy a mobilní zařízení s omezenou RAM.
ArrayBuffer slouží pro binární data, ale nemá metody pro ruční uvolnění nebo změnu velikosti. Návrhy jako ArrayBuffer.transfer() nejsou implementovány ve většině prohlížečů. Odpojený stav bufferu (byteLength=0, přístup vyvolá TypeError) umožňuje GC rychle zpracovat prázdné buffery.
Trik s MessageChannel pro odpojení ArrayBuffer
MessageChannel přenáší data mezi kontexty. Uzavření port2 způsobí, že port1 se uvolní: data jdou k GC bez čekání na cyklus.
const Koš = (() => {
/** @type {MessageChannel | null} */
let kanál = null;
/**
* Zrychluje uvolňování paměti pro ArrayBuffer, TypedArray, DataView.
* @param {any} odpad
*/
function vyhodit(odpad) {
if (typeof odpad === 'object' && odpad) {
const buffer = odpad.buffer || odpad;
if (buffer.byteLength) {
if (!kanál) {
kanál = new MessageChannel();
kanál.port2.close();
}
kanál.port1.postMessage(buffer, [buffer]);
}
}
}
return {vyhodit};
})();
Použití:
let buffer = new ArrayBuffer(1_000_000);
// Práce s bufferem...
Koš.vyhodit(buffer);
buffer = null;
Kompatibilita: Funguje v Chrome 55+, Firefox 50+. V Chrome 56 se data zasekávají. Výsledek: snížení špičky paměti o 100 MB v reálném scénáři.
Omezení: Ne pro asm.js hromady (Firefox 49). Nestabilita mezi verzemi.
Trik s Workerem pro dávkové uvolňování
Worker.postMessage převádí ArrayBuffer do odpojeného stavu. Ukončení workera (self.close()) okamžitě uvolní celou paměť vlákna.
Optimalizace: hromadíme ~10 MB před ukončením, minimalizujeme režii na vytváření/zničení.
const Koš = (() => {
const KAPACITA_KOŠE = 10_000_000;
/** @type {Worker | null} */
let worker = null;
let urlWorkera = '';
let bajtyVWorkerovi = 0;
function vyhodit(odpad) {
if (typeof odpad === 'object' && odpad) {
const buffer = odpad.buffer || odpad;
if (buffer.byteLength) {
if (!worker) {
if (!urlWorkera) {
urlWorkera = URL.createObjectURL(new Blob([
`'use strict'; self.onmessage = ({data}) => { if (!data) self.close(); };`
], {type: 'application/javascript'}));
}
worker = new Worker(urlWorkera, {name: 'Koš'});
}
bajtyVWorkerovi += buffer.byteLength;
worker.postMessage(buffer, [buffer]);
if (bajtyVWorkerovi > KAPACITA_KOŠE) {
vyčistit();
}
}
}
}
function vyčistit() {
if (worker) {
worker.postMessage(null);
worker = null;
bajtyVWorkerovi = 0;
}
}
return {vyhodit, vyčistit};
})();
Volat Koš.vyčistit() při ukončení práce. Výsledek: Chrome -310 MB, Firefox -70 MB špičky paměti.
Problémy: Únik virtuálního adresního prostoru ve Firefoxu 55+.
Srovnání výkonu a doporučení
Testování na 40 minut: CPU cykly v rozptylu chyby. MessageChannel je pomalejší než Worker ve Firefoxu (syntetika).
| Trik | Efekt v Chrome | Efekt ve Firefoxu | Kompatibilita |
|---------------|----------------|-------------------|-------------------|
| MessageChannel| -100 MB | Stabilní | Nízká |
| Worker | -310 MB | -70 MB | Vysoká (kromě Firefox 55+) |
- Používejte Worker pro produkci: stabilnější, větší efekt v Chrome.
- Testujte v cílových prohlížečích: algoritmy GC se mění.
- Vyhněte se pro asm.js/WebAssembly hromady.
- Monitorujte perfmon/process explorer pro working set.
Worker je lepší při vysokých zátěžích (video, streamy).
Co je důležité
- Oba triky převádějí ArrayBuffer do odpojeného stavu přes postMessage, zrychluje GC.
- Worker hromadí 10 MB pro dávkování, minimalizuje režii.
- Efekt: až 310 MB snížení špičky v Chrome při streamingu.
- Režie na CPU <1 % v reálných scénářích.
- Testujte kompatibilitu: změny GC láme triky mezi verzemi.
— Editorial Team
Zatím žádné komentáře.