Zpět na domů

Správa paměti ArrayBuffer v JS: triky

Článek rozebírá techniky zrychlení uvolňování velkých ArrayBuffer v JS prostřednictvím detached stavu s MessageChannel a Worker. Jsou ukázány kód, výsledky testů (-310 MB v Chrome) a kompatibilita. Pro high-load aplikace jako video-streamy.

Triky uvolňování paměti ArrayBuffer v prohlížečích
Advertisement 728x90

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.

Google AdInline article slot
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.

Google AdInline article slot

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.

Google AdInline article slot

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

Advertisement 728x90

Číst dál