ECMAScript2021 adds new features WeakRef and FinalizationRegistry


Javascript utilizes garbage collection (GC) for automatic memory management. The purpose of a garbage collector is to monitor memory allocation and determine when a block of allocated memory is no longer needed and reclaim it. The object is not collected, by the garbage collector as long as a reference to that object exists. This keeps the object in memory, leaving less memory for usage.

ECMAScript2021 added WeakRef and FinalizationRegistry features to handle garbage collection properly.

Let’s check out these concepts in detail.

WeakRef

WeakRef is the new enhancement in the world of objects in Javascript. As the name suggests, WeakRef contains the weak reference to the Javascript object. A normal object is not collected by the garbage collector as long as a reference to that object exists. It keeps the object in memory.

But, the Javascript object collector clears the WeakRef reference and reclaims the memory.

We can create a Weak Reference by using new WeakRef, and can read a reference by calling the deref() method.

const weakrefObj = new WeakRef({ counterFlag: 1 });
const interval = setInterval(() => {
  const derefObj = weakrefObj.deref() // extracting the values of deref.
    if(derefObj) { //checking if deref exists, if it does it will print and increment the counter)
      document.write(derefObj.counterFlag);
      derefObj.counterFlag++;
    }
    else { // else it will stop the interval with message
      document.write("weakref gone");
      clearInterval(interval);
    }
  }, 1000);

FinalizationRegistry

FinalizationRegistry works as a cleaner after the garbage collection. As the memory is reclaimed by the garbage collector, it is needed to be cleaned afterward. FinalizationRegistry works as a cleanup callback.

Instance Methods:

register(ObjectNeededToBeCleared, heldValue, TokenObject):

Registers target to this so that cleanup callback will be called.

unregister(TokenObject):

Unregisters the registered token object and prevents the cleanup callback from happening if the token object is passed inside unregister method parameter.

let isObjectCleanedUp = true; // Flag to check if object is cleaned up or not.

const registry = new FinalizationRegistry((heldValue) =>
{
  // Registry method which called after the object
  console.log(`cleanup: ${heldValue}`);
  isObjectCleanedUp = false;
});

let objectTobeHandled = {};
const fakeTokenRegistered = {};
const fakeTokenUnregistered = {};
// Registering the object with FakeTokenRegisterObject
registry.register(objectTobeHandled, 'registered with token1', fakeTokenRegistered);
// Registering the object with FakeTokenUnRegisterObject
registry.register(objectTobeHandled, 'registered with token2', fakeTokenUnregistered);
//Clearing the hard reference for garbage collection.
objectTobeHandled = undefined;
// Unregister the fakeTokenUnregisterObject
registry.unregister(fakeTokenUnregistered);

async function registeryHandler() {
  console.log("cleaning up timer starts now")
  let timerInSeconds = 0;
  const objectChekcInterval = setInterval(() => {
    if (isObjectCleanedUp) {
      console.log(`objectTobeHandled is there for ${timerInSeconds} seconds`);
      timerInSeconds++;
    } else {
      console.log(`objectTobeHandled is claimed in ${timerInSeconds} seconds`);
      clearInterval(objectChekcInterval);
    }
  }, 1000)
}

registeryHandler()

In the above example, we have unregistered the FakeTokenUnRegisterObject. It will not print the console of that line, instead will keep printing the console of how many seconds it happened to clean up the memory.

Note:

The finalization callback does not run immediately after garbage-collection. The timing of garbage collection and finalization callbacks is unspecified. Even though the callback can be useful in some cases, it is advised to avoid it, for important logic or metrics.