Zurück zur Startseite

ArrayBuffer-Speicherverwaltung in JS: Tricks

Der Artikel zerlegt Techniken zur Beschleunigung der Freigabe großer ArrayBuffer in JS durch detached-Zustand mit MessageChannel und Worker auf. Zeigt Code, Testergebnisse (-310 MB in Chrome) und Kompatibilität. Für High-Load-Anwendungen wie Video-Streams.

ArrayBuffer-Freigabe-Tricks in Browsern
Advertisement 728x90

ArrayBuffer-Speicherfreigabe in JavaScript beschleunigen

Der Garbage Collector von JavaScript verwaltet den Speicher automatisch, doch bei der kontinuierlichen Verarbeitung großer ArrayBuffers (mehrere MB pro Sekunde) entstehen Verzögerungen. In Browsern wie Chrome schießt der Speicherverbrauch auf Hunderte von MB in die Höhe, besonders bei Videostreaming. Firefox meistert das besser, aber Spitzen treten dennoch auf. Das ist entscheidend für 32-Bit-Prozesse und Mobilgeräte mit begrenztem RAM.

ArrayBuffer ist für Binärdaten konzipiert, bietet aber keine manuellen Freigabe- oder Größenänderungsfunktionen. Vorschläge wie ArrayBuffer.transfer() sind nicht weit verbreitet. Der detached-Zustand eines Buffers (byteLength=0, Zugriff wirft TypeError) ermöglicht es dem GC, ihn schnell zurückzugewinnen.

MessageChannel-Trick zum Abtrennen von ArrayBuffers

MessageChannel überträgt Daten zwischen Kontexten. Das Schließen von port2 trennt port1: Die Daten landen direkt beim GC, ohne auf den Event Loop zu warten.

Google AdInline article slot
const GarbageBin = (() => {
    /** @type {MessageChannel | null} */
    let messageChannel = null;

    /**
     * Beschleunigt die Speicherfreigabe für ArrayBuffer, TypedArray, DataView.
     * @param {any} junk
     */
    function dispose(junk) {
        if (typeof junk === 'object' && junk) {
            const buffer = junk.buffer || junk;
            if (buffer.byteLength) {
                if (!messageChannel) {
                    messageChannel = new MessageChannel();
                    messageChannel.port2.close();
                }
                messageChannel.port1.postMessage(buffer, [buffer]);
            }
        }
    }

    return { dispose };
})();

Verwendung:

let buffer = new ArrayBuffer(1_000_000);
// Arbeiten mit buffer...
GarbageBin.dispose(buffer);
buffer = null;

Kompatibilität: Funktioniert in Chrome 55+, Firefox 50+. Chrome 56 hat Probleme mit hängenden Daten. Ergebnis: Reduktion des Spitzen-Speichers um 100 MB in realen Szenarien.

Einschränkungen: Nicht für asm.js-Heaps (Firefox 49). Instabil über Versionen hinweg.

Google AdInline article slot

Worker-Trick für Stapelspeicherbereinigung

Worker.postMessage trennt ArrayBuffer. Das Beenden des Workers (self.close()) gibt sofort allen Thread-Speicher frei.

Optimierung: Sammle ~10 MB vor dem Herunterfahren, um Erstellungs-/Zerstörungsaufwand zu minimieren.

const GarbageBin = (() => {
    const BIN_CAPACITY = 10_000_000;

    /** @type {Worker | null} */
    let worker = null;
    let workerURL = '';
    let bytesInWorker = 0;

    function dispose(junk) {
        if (typeof junk === 'object' && junk) {
            const buffer = junk.buffer || junk;
            if (buffer.byteLength) {
                if (!worker) {
                    if (!workerURL) {
                        workerURL = URL.createObjectURL(new Blob([
                            `'use strict'; self.onmessage = ({data}) => { if (!data) self.close(); };`
                        ], {type: 'application/javascript'}));
                    }
                    worker = new Worker(workerURL, {name: 'GarbageBin'});
                }
                bytesInWorker += buffer.byteLength;
                worker.postMessage(buffer, [buffer]);
                if (bytesInWorker > BIN_CAPACITY) {
                    clear();
                }
            }
        }
    }

    function clear() {
        if (worker) {
            worker.postMessage(null);
            worker = null;
            bytesInWorker = 0;
        }
    }

    return { dispose, clear };
})();

GarbageBin.clear() beim Herunterfahren aufrufen. Ergebnis: Chrome -310 MB, Firefox -70 MB Spitzen-Speicher.

Google AdInline article slot

Probleme: Virtuelle Adressraumauslaufen in Firefox 55+.

Leistungsvergleich und Empfehlungen

Getestet über 40 Minuten: CPU-Zyklen im Messfehlerbereich. MessageChannel langsamer als Worker in Firefox (synthetische Tests).

| Trick | Chrome-Effekt | Firefox-Effekt | Kompatibilität |

|---------------|---------------|----------------|-------------------|

| MessageChannel | -100 MB | Stabil | Gering |

| Worker | -310 MB | -70 MB | Hoch (auß. FF 55+)|

  • Worker für Produktion nutzen: Zuverlässiger, größerer Effekt in Chrome.
  • In Zielbrowsern testen: GC-Algorithmen entwickeln sich weiter.
  • Für asm.js/WebAssembly-Heaps vermeiden.
  • Mit Perfmon/Prozess-Explorer überwachen für Working Set.

Worker glänzt bei hohen Lasten (Video, Streams).

Wichtige Erkenntnisse

  • Beide Tricks trennen ArrayBuffer via postMessage und beschleunigen den GC.
  • Worker stapelt 10 MB, um Overhead zu reduzieren.
  • Effekt: Bis zu 310 MB Spitzenreduktion bei Chrome-Streaming.
  • CPU-Overhead <1% in realen Szenarien.
  • Kompatibilität testen: GC-Änderungen brechen Tricks über Versionen.

— Editorial Team

Advertisement 728x90

Weiterlesen