Aller au contenu principal

Electron Internals: Weak References

· 6 mins à lire

As a language with garbage collection, JavaScript frees users from managing resources manually. But because Electron hosts this environment, it has to be very careful avoiding both memory and resources leaks.

This post introduces the concept of weak references and how they are used to manage resources in Electron.

Weak references

In JavaScript, whenever you assign an object to a variable, you are adding a reference to the object. As long as there is a reference to the object, it will always be kept in memory. Once all references to the object are gone, i.e. there are no longer variables storing the object, the JavaScript engine will recoup the memory on next garbage collection.

A weak reference is a reference to an object that allows you to get the object without effecting whether it will be garbage collected or not. You will also get notified when the object is garbage collected. It then becomes possible to manage resources with JavaScript.

Using the NativeImage class in Electron as an example, every time you call the nativeImage.create() API, a NativeImage instance is returned and it is storing the image data in C++. Once you are done with the instance and the JavaScript engine (V8) has garbage collected the object, code in C++ will be called to free the image data in memory, so there is no need for users manage this manually.

Un autre exemple est le problème de disparition de fenêtre, qui montre visuellement comment la fenêtre est des ordures collectées quand toutes les références à lui sont terminées.

Testing weak references in Electron

There is no way to directly test weak references in raw JavaScript since the language doesn't have a way to assign weak references. Il n'y a aucun moyen de tester directement les références faibles en JavaScript brut, puisque le langage n'a pas de moyen d'assigner des références faibles.

In versions of Electron prior to v0.37.8, you can use the internal v8Util.setDestructor API to test weak references, which adds a weak reference to the passed object and calls the callback when the object is garbage collected:

// Le code ci-dessous ne peut s'exécuter que sur Electron < v0.37.8.
var v8Util = process.atomBinding('v8_util')

var objet = {}
v8Util. etDestructor(object, function () {
console.log('L'objet est garbage collected')

// Supprime toutes les références à l'objet.
object = undefined
// Démarre manuellement un GC. gc()
// La console affiche "L'objet est ramassé".

Note that you have to start Electron with the --js-flags="--expose_gc" command switch to expose the internal gc function.

The API was removed in later versions because V8 actually does not allow running JavaScript code in the destructor and in later versions doing so would cause random crashes.

Weak references in the remote module

Apart from managing native resources with C++, Electron also needs weak references to manage JavaScript resources. Un exemple est le module distantd'Electron, , qui est un module d'appel à distance (RPC) qui permet d'utiliser des objets dans le processus principal à partir des processus de rendu.

One key challenge with the remote module is to avoid memory leaks. When users acquire a remote object in the renderer process, the remote module must guarantee the object continues to live in the main process until the references in the renderer process are gone. Additionally, it also has to make sure the object can be garbage collected when there are no longer any reference to it in renderer processes.

For example, without proper implementation, following code would cause memory leaks quickly:

const {remote} = require('electron')

for (let i = 0; i < 10000; ++i) {

The resource management in the remote module is simple. Whenever an object is requested, a message is sent to the main process and Electron will store the object in a map and assign an ID for it, then send the ID back to the renderer process. In the renderer process, the remote module will receive the ID and wrap it with a proxy object and when the proxy object is garbage collected, a message will be sent to the main process to free the object.

En utilisant l'API remote.require comme exemple, une implémentation simplifiée semble comme cela:

remote.require = function (name) {
// Tell the main process to return the metadata of the module.
const meta = ipcRenderer.sendSync('REQUIRE', name)
// Create a proxy object.
const object = metaToValue(meta)
// Tell the main process to free the object when the proxy object is garbage
// collected.
v8Util.setDestructor(object, function () {
return object

Dans le processus principal :

const map = {}
const id = 0

ipcMain.on('REQUIRE', function (event, name) {
const object = require(name)
// Add a reference to the object.
map[++id] = object
// Convert the object to metadata.
event.returnValue = valueToMeta(id, object)

ipcMain.on('FREE', function (event, id) {
delete map[id]

Maps with weak values

With the previous simple implementation, every call in the remote module will return a new remote object from the main process, and each remote object represents a reference to the object in the main process.

The design itself is fine, but the problem is when there are multiple calls to receive the same object, multiple proxy objects will be created and for complicated objects this can add huge pressure on memory usage and garbage collection.

For example, the following code:

const {remote} = require('electron')

for (let i = 0; i < 10000; ++i) {

It first uses a lot of memory creating proxy objects and then occupies the CPU (Central Processing Unit) for garbage collecting them and sending IPC messages.

An obvious optimization is to cache the remote objects: when there is already a remote object with the same ID, the previous remote object will be returned instead of creating a new one.

This is not possible with the API in JavaScript core. Utiliser la carte normale pour mettre en cache des objets empêchera V8 de ramasser les objets, alors que la classe WeakMap ne peut utiliser que des objets comme clés faibles.

To solve this, a map type with values as weak references is added, which is perfect for caching objects with IDs. Now the remote.require looks like this:

const remoteObjectCache = v8Util.createIDWeakMap()

remote.require = function (name) {
// Tell the main process to return the meta data of the module.
if (remoteObjectCache.has(
return remoteObjectCache.get(
// Create a proxy object.
remoteObjectCache.set(, object)
return object

Notez que le remoteObjectCache conserve des objets comme des références faibles, il n’est donc pas nécessaire de supprimer la clé lorsque l’objet est collecté par le garbage collector.

Native code

For people interested in the C++ code of weak references in Electron, it can be found in following files:

The setDestructor API:

The createIDWeakMap API: