Prevent JavaScript Memory Leaks with URL.revokeObjectURL

In modern web applications, handling binary data like images, PDFs, and video streams directly in the browser is common. The URL.createObjectURL() method allows developers to create temporary URLs referencing Blob or File objects stored in browser memory. However, because the browser retains strong internal references to these objects for the lifetime of the document, failing to release them leads to significant memory leaks. Calling URL.revokeObjectURL() explicitly releases these references, allowing the JavaScript garbage collector to reclaim unused memory and maintain optimal application performance.

How Object URLs Work

When you invoke URL.createObjectURL(blob), the browser creates a unique blob: URL that acts as an internal pointer to the underlying binary data. This allows you to treat local binary objects like standard URL resources in elements such as <img>, <video>, or <a> download links without uploading them to a server first.

const blob = new Blob([largeDataArray], { type: 'application/octet-stream' });
const blobUrl = URL.createObjectURL(blob);

const link = document.createElement('a');
link.href = blobUrl;
link.download = 'file.bin';
link.click();

The Cause of the Memory Leak

JavaScript relies on automatic garbage collection (GC) to free memory that is no longer reachable. However, object URLs create an exception to standard scope-based GC rules:

  1. Persistent Internal Mapping: The browser maintains an internal mapping from the generated blob: URL string to the in-memory Blob object.
  2. Document-Bound Lifetime: This internal reference remains alive until the current document is completely unloaded, even if your application’s JavaScript variables referencing the Blob go out of scope.
  3. Single Page Application (SPA) Vulnerability: In traditional multi-page websites, navigation unloads the document and clears the memory. In SPAs, the document remains open indefinitely, meaning every unrevoked object URL continues to consume RAM across user sessions.

If a user generates previews for high-resolution images or downloads multiple files over time, unrevoked object URLs will continuously consume RAM, eventually leading to UI lag, tab crashes, or performance degradation on memory-constrained mobile devices.

The Role of URL.revokeObjectURL

URL.revokeObjectURL() informs the browser that the internal URL mapping is no longer needed. Once revoked, the browser removes the association, allowing the garbage collector to safely free the memory allocated to the Blob (provided no other active JavaScript references exist).

URL.revokeObjectURL(blobUrl);

Once revoked, attempting to load or fetch the blob: URL will result in a 404 (Not Found) error.

Best Practices for Revoking Object URLs

To prevent leaks without breaking media rendering or file operations, apply revocation at the right time in your application lifecycle:

1. Immediately After Image or Media Loads

When generating URLs for media elements, revoke the URL as soon as the element finishes loading:

const img = document.createElement('img');
const objectUrl = URL.createObjectURL(imageBlob);

img.onload = () => {
  URL.revokeObjectURL(objectUrl);
};

img.src = objectUrl;
document.body.appendChild(img);

2. Immediately After Triggering Downloads

When generating dynamic files for download, revoke the URL immediately after triggering the synthetic click event:

function downloadData(data, filename) {
  const blob = new Blob([data], { type: 'text/plain' });
  const url = URL.createObjectURL(blob);
  
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  
  URL.revokeObjectURL(url);
}

3. During Component Unmount (SPA Frameworks)

In modern frameworks like React, Vue, or Angular, ensure object URLs are revoked in lifecycle cleanup hooks:

useEffect(() => {
  const url = URL.createObjectURL(file);
  setPreviewUrl(url);

  return () => {
    URL.revokeObjectURL(url);
  };
}, [file]);

Summary

URL.revokeObjectURL provides essential manual memory management within JavaScript’s garbage-collected environment. By promptly revoking every created object URL, you prevent silent memory accumulation and ensure your web applications remain fast, stable, and responsive.