Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Javascript e Garbage Collector

Sfruttiamo la classe WeakMap di Javascript per verificare (in ambiente di test e debug) se un oggetto è stato "rimosso" dal garbage collector del browser.
Javascript e Garbage Collector
Sfruttiamo la classe WeakMap di Javascript per verificare (in ambiente di test e debug) se un oggetto è stato "rimosso" dal garbage collector del browser.
Link copiato negli appunti

Quando si programmano applicazioni in Javascript, capita spesso di "dimenticarsi" del consumo della memoria, affidandosi alla "magica" capacità del browser (o anche del sistema operativo, in base al contesto applicativo - vedi Node.js) di gestirlo. In realtà non sempre è così, e per applicazioni più complesse capita di doversi chiedere come venga gestito questo aspetto.

Seguendo alcune idee discusse in un post di Steve Hanov, è possibile verificare se un oggetto Javascript è stato "distrutto" (e quindi rimosso dalla memoria) dal garbage collector del browser. Sfortunatamente, il metodo funziona con Chrome ed altri browser, ma non con Firefox. Rimane tuttavia un utile strumento di test e debug.

Il codice

Il codice che segue sfrutta la classe WeakMap per associare 1 GB di memoria ad un qualsiasi altro oggetto. Nel momento in cui quest'ultimo viene eliminato dal garbage collector, verrà rimosso anche il GB associato ad esso. Sfruttando la possibilità (offerta da Chrome) di verificare la quantità di memoria usata, è possibile analizzare se l'oggetto che ci interessa analizzare è stato liberato o meno.

In aggiunta a quanto detto, si noterà la presenza di un timeout che agisce ogni 10 secondi. La spiegazione di ciò è dovuta al fatto che Chrome esegue il garbage collector ad intervalli più o meno regolari, proprio di 10 secondi.

/** Determines if an object is freed
@param obj is the object of interest
@param freeFn is a function that frees the object.
@returns a promise that resolves to {freed: boolean, memoryDiff:number}
@author Steve Hanov <steve.hanov@gmail.com>
*/
function isObjectFreed(obj, freeFn) {
  return new Promise( (resolve) => {
    if (!performance.memory) {
      throw new Error("Browser not supported.");
    }
    // When obj is GC'd, the large array will also be GCd and the impact will
    // be noticeable.
    const allocSize = 1024*1024*1024;
    const wm = new WeakMap([[obj, new Uint8Array(allocSize)]]);
    // wait for memory counter to update
    setTimeout( () => {
      const before = performance.memory.usedJSHeapSize;
      // Free the memory
      freeFn();
      // wait for GC to run, at least 10 seconds
      setTimeout( () => {
        const diff = before - performance.memory.usedJSHeapSize;
        resolve({
          freed: diff >= allocSize,
          memoryDiff: diff - allocSize
        });
      }, 10000);
    }, 100);
  });
}
let foo = {bar:1};
isObjectFreed(foo, () => foo = null).then( (result) => {
  document.write(`Object GCd:${result.freed}, ${result.memoryDiff} bytes freed`)
}, (error) => {
  document.write(`Error: ${error.message}`)
})

L'associazione di 1 GB di memoria all'oggetto da analizzare, nasce dalla necessità di dover potere verificare una differenza significativa nella quantità di memoria usata. Senza questo "trucchetto", la differenza non sarebbe significativa, e l'analisi di memoria diventerebbe complicata (se non impossibile).

È ovvio notare che non è certamente l'ideale "sprecare" 1 GB di memoria in un ambiente di produzione. Ciononostante, quello appena visto rimane un utile esempio applicabile in fase di test e debug, soprattutto per applicazioni che utilizzano oggetti di difficile gestione, "immersi" in framework come React, Vue.js o Angular.

Ti consigliamo anche