DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Enterprise AI Trend Report: Gain insights on ethical AI, MLOps, generative AI, large language models, and much more.

2024 Cloud survey: Share your insights on microservices, containers, K8s, CI/CD, and DevOps (+ enter a $750 raffle!) for our Trend Reports.

PostgreSQL: Learn about the open-source RDBMS' advanced capabilities, core components, common commands and functions, and general DBA tasks.

AI Automation Essentials. Check out the latest Refcard on all things AI automation, including model training, data security, and more.

Related

  • The Import Statement With an Emscripten-Generated WebAssembly Module in Vue.js
  • Unleashing the Power of WebAssembly to Herald a New Era in Web Development
  • A Comprehensive Guide To Working With JSON in JavaScript
  • JavaScript's Secret Weapon: Unraveling the Mysteries of Proxies

Trending

  • Data Processing in GCP With Apache Airflow and BigQuery
  • Modern Digital Authentication Protocols
  • Implement RAG Using Weaviate, LangChain4j, and LocalAI
  • How to Query XML Files Using APIs in Java
  1. DZone
  2. Coding
  3. JavaScript
  4. What I Learned About the WebAssembly Memory Model and How to Debug Memory Leaks

What I Learned About the WebAssembly Memory Model and How to Debug Memory Leaks

The WebAssembly memory model is different from JavaScript and it's easy to create memory leaks. This article talks about how to prevent Wasm memory leaks and find them.

By 
Andrew Burnett-Thompson user avatar
Andrew Burnett-Thompson
·
Dec. 30, 23 · Analysis
Like (2)
Save
Tweet
Share
3.7K Views

Join the DZone community and get the full member experience.

Join For Free

So I've been working on a project for a while to create a real-time, high-performance JavaScript Chart Library. This project uses quite an ambitious & novel tech stack including a large legacy codebase in C/C++ which is compiled to WebAssembly using Emscripten, targetting WebGL, and a TypeScript API wrapper allowing you to load the charts in JS without having to worry about the underlying Wasm. 

First Up, Why Use Wasm at All?

WebAssembly is an exciting technology and offers performance benefits over JavaScript in many cases. Also, in this case, a legacy C++ codebase already handled much of the rendering for charts & graphs in OpenGL and needed only a little work to be able to target WebGL. 

It's fairly easy to compile existing C++ code into WebAssembly using Emscripten and all that remains is writing bindings to generate Typings and then your JavaScript API around the Wasm library to use it.

During the development of the library we learned some interesting things about the WebAssembly memory model, how to avoid and debug memory leaks which I'll share below.

JavaScript vs. WebAssembly Memory Model

WebAssembly has a completely different memory model to JavaScript. While JavaScript has a garbage collector, which automatically cleans up the memory of variables that are no longer required, WebAssembly simply does not. An object or buffer declared in Wasm memory must be deleted by the caller, if not a memory leak will occur. 

How Memory Leaks Are Caused in JavaScript

Memory leaks can occur in both JavaScript and WebAssembly and care and attention must be taken by the developer to ensure that memory is correctly cleaned up when using WebAssembly.

Despite being a Garbage-Collected managed programming language, it’s still extremely easy to create a memory leak just in vanilla JavaScript. Here are a couple of ways that is possible to inadvertently leak memory in a JavaScript app:

  • Arrow functions and closure can capture variables and keep them alive, so they cannot be deleted by the JavaScript garbage collector
  • Callbacks or event listeners can capture a variable and keep it alive.
  • Global variables or static variables stay alive for the lifetime of the application. Simply forgetting to use let or const can convert a variable to a global variable.
  • Even detached DOM nodes can keep objects alive in JavaScript. Simply removing a node from the DOM but keeping a variable to it can prevent the node and its children from being collected.

How Memory Leaks Are Caused in WebAssembly

Wasm has a separate heap from the JavaScript virtual machine. This memory is allocated in the browser, and reserved from the host OS. When you allocate memory in Wasm, the Wasm heap is grown, and a range of addresses are reserved. When you delete memory in Wasm, the heap does not shrink and memory is not returned to the host OS. Instead, the memory is simply marked as deleted or available. This means it can be re-used by future allocations.

To cause a memory leak in WebAssembly you simply need to allocate memory and forget to delete it. Since there is no automatic garbage collection, finalization, or marking of memory as no longer needed, it must come from the user. All WebAssembly types exported by the compiler Emscripten have a function .delete() on objects that use Wasm memory. This needs to be called when the object is no longer required. Here's a quick example:

Example: Leaking Memory in Wasm

Assuming you have a type declared in C++ like this:

C++
 
// person.cpp
#include <string>

class Person {
public:
// C++ Constructor
  Person(std::string name, int age) : name(name), age(age) {}

  // C++ Destructoe
  ~Person() {}

  std::string getName() { return name; }
  int getAge() { return age; }

private:
   std::string name;
   int age;
};


And compile and export the type using Emscripten like this

CMake
 
emcc person.cpp -o person.js -s EXPORTED_FUNCTIONS="['_createPerson', '_deletePerson', '_getName', '_getAge']" -s MODULARIZE=1


You can now instantiate, use, and delete the type in JavaScript like this:

JavaScript
 
const Module = require('./person.js'); // Include the generated JavaScript interface

Module.onRuntimeInitialized = () => {

  // Instantiate a Person object
  const person = new Module.Person('John Doe', 30);
  console.log('Person object created:', person);

  // Access and print properties
  console.log('Name:', person.getName());
  console.log('Age:', person.getAge());

  // Delete the Person object (calls the C++ destructor)
  person.delete();
};


Forgetting to call, however, causes a Wasm memory leak. The memory in the browser will grow and not shrink.

Detecting Memory Leaks in WebAssembly Applications

Because a memory leak is catastrophic to an application, we had to ensure that our code did not leak memory, but also the user code (those consuming and using our [JavaScript Chart Library](https://www.scichart.com/javascript-chart-features) in their applications) did not leak memory. 

To solve this we developed our in-house memory debugging tools. This is implemented as an object registry which is a Map<string, TObjectEntryInfo> of all objects undeleted and uncollected where TObjectEntryInfo is a type which stores WeakRef to the object.

Using a JavaScript proxy technique we were able to intercept calls to new/delete on all WebAssembly types. Each time an object was instantiated, we added it to the objectRegistry and each time it was deleted, we removed it from the objectRegistry.

Now you can run your application, enable the memory debugging tools, and output specific snapshots of your application state. Here's an example of the tool's output. 

First, enable the MemoryUsageHelper (memory debugging tools)

JavaScript
 
import { MemoryUsageHelper} from "scichart";

MemoryUsageHelper.isMemoryUsageDebugEnabled = true;


This automatically tracks all the types in our library, but you can track any arbitrary object in your application by calling register and unregister like this:

JavaScript
 
// Register an arbitrary object
MemoryUsageHelper.register(yourObject, "identifier");

// Unregister an arbitrary object
MemoryUsageHelper.unregister("identifier");


Later, at a specific point output a snapshot by calling this function:

JavaScript
 
MemoryUsageHelper.objectRegistry.log();


This outputs to the console all the objects which have not been deleted, or uncollected

console output

How To Use This Output

  1. Objects that are in the undeletedObjectsMap may either be still alive or perhaps you've forgotten to delete them. In this case, the resolution is simple. Call .delete() on the object when you are done with it.
  2. Objects in uncollectedObjectsMap have not yet been garbage collected. This could be a traditional JS memory leak (which also affects Wasm memory) so check for global variables, closure, callbacks, and other causes of traditional JS memory leaks.
  3. Objects in deletedNotCollected and collectedNotDeleted identify possible leaks where an object was collected by the javascript garbage collector but not deleted (and vice versa).

MemoryUsageHelper Wasm Memory leak debugging tools are part of SciChart.js, available on npm with a free community license.
It can be used in WebAssembly applications or JavaScript applications to track memory usage.

JavaScript WebAssembly Debug (command) garbage collection Object (computer science)

Published at DZone with permission of Andrew Burnett-Thompson. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • The Import Statement With an Emscripten-Generated WebAssembly Module in Vue.js
  • Unleashing the Power of WebAssembly to Herald a New Era in Web Development
  • A Comprehensive Guide To Working With JSON in JavaScript
  • JavaScript's Secret Weapon: Unraveling the Mysteries of Proxies

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: