Garbage Collection Mechanism
We know that there will be some rubbish data that is no longer in use and needs to be released in time. If we don't release it in time, this is a memory leak
The garbage data in JS is automatically collected by the Garbage Collection (abbreviated as GC) and does not need to be released manually. How does it mumble?
Simply, there is a background process in the JS engine called the garbage collector that monitors all objects, observes whether they are accessible, and periodically deletes those that are inaccessible at regular intervals.
Now browsers commonly use two methods of garbage collection:
- Reference Count
- Marker Cleanup
Reference Count
The earliest and easiest garbage collection mechanism is to attach a reference counter to an object that occupies physical space. When another object references this object, the reference count of the object is added by one, and when the object's reference count is removed by one, and when the object's reference count is 0, it is recycled.
It's easy, but it can cause memory leaks:
// Circular Reference Issues function temp(){ var a={}; var b={}; a.o = b; b.o = a; }
In this case, each time the temp function is called, the reference counts for a and b are 2, which means that this part of memory will never be freed, that is, memory leaks. It is rarely used now, and only lower versions of IE use it.
Marker Cleanup
In V8, the main garbage collector recycles garbage by means of label clearance. The main processes are as follows:
- Tag: Traverse the call stack to see if the objects in the old generation area stack are referenced, the referenced objects are marked as active objects, and the unreferenced objects (to be cleaned up) are marked as garbage data.
- Garbage Cleanup: Clean up all garbage data
(Image source: How JavaScript works: memory management + how to handle 4 common memory leaks)
In our development process, if we want the garbage collector to recycle an object, we set the object's reference directly to null
var a = {}; // {} is accessible, a is its reference a = null; // Reference set to null // {} will be cleared from memory
However, if an object is referenced multiple times, such as as as as a key, value, or child element of another object, when the object reference is set to null, the object is not recycled and still exists
var a = {}; var arr = [a]; a = null; console.log(arr) // [{}]
What if it's the key to Map?
var a = {}; var map = new Map(); map.set(a, 'Three Minute Front End') a = null; console.log(map.keys()) // MapIterator {{}} console.log(map.values()) // MapIterator {"Three Minute Front End"}
What if you want a to be null and the object is recycled?
WeakMap vs Map
ES6 took this into account and launched WeakMap. It does not include references to values in the garbage collection mechanism, so there will only be a Weak in the name, indicating that this is a weak reference (a weak reference to an object means that it will not prevent GC recycling when the object should be recycled by the GC).
Map versus WeakMap:
- Map keys can be any type, WeakMap only accepts objects as keys (except null) and does not accept values of other types as keys
- Map keys are actually bound to memory addresses, which are treated as two keys as long as the memory addresses are different. WeakMap's key is a weak reference, the object it points to can be garbage collected, and the key is invalid
- Maps can be traversed, WeakMap s cannot be traversed
Let's take WeakMap as an example to see how it works:
var a = {}; var map = new WeakMap(); map.set(a, 'Three Minute Front End') map.get(a) a = null;
What can't be seen in the above example? We go through the process.memoryUsage to test:
//map.js global.gc(); // 0 gc() followed by memory Usage() is executed for each query to ensure garbage collection and accurate memory usage function usedSize() { const used = process.memoryUsage().heapUsed; return Math.round((used / 1024 / 1024) * 100) / 100 + "M"; } console.log(usedSize()); // 1 Initial state, heapUsed value is 1.64M after gc() and memoryUsage() are executed var map = new Map(); var b = new Array(5 * 1024 * 1024); map.set(b, 1); global.gc(); console.log(usedSize()); // 2 After adding element b to Map, an array of 5*1024*1024, heapUsed is about 41.82M b = null; global.gc(); console.log(usedSize()); // 3 After leaving b blank, heapUsed remains 41.82M, indicating that the array in the Map with a length of 5*1024*1024 still exists
Execute node --expose-gc map.js command:
Where the --expose-gc parameter indicates that the garbage collection mechanism is allowed to be executed manually
// weakmap.js function usedSize() { const used = process.memoryUsage().heapUsed; return Math.round((used / 1024 / 1024) * 100) / 100 + "M"; } global.gc(); // 0 gc() followed by memory Usage() is executed for each query to ensure garbage collection and accurate memory usage console.log(usedSize()); // 1 Initial state, heapUsed value is 1.64M after gc() and memoryUsage() are executed var map = new WeakMap(); var b = new Array(5 * 1024 * 1024); map.set(b, 1); global.gc(); console.log(usedSize()); // 2 After adding element b to Map, an array of 5*1024*1024, heapUsed is about 41.82M b = null; global.gc(); console.log(usedSize()); // 3 When b is empty, heapUsed becomes about 1.82M, indicating that the array of length 5*1024*1024 in WeakMap has been destroyed
Execute node --expose-gc weakmap.js command:
In the code above, as long as the external references disappear, the references inside WeakMap are automatically cleared by garbage collection. Thus, with its help, it is much easier to solve memory leaks.
Take a final look at WeakMap
WeakMap
A WeakMap object is a collection of key-value pairs where the key is a weakly referenced object and the value can be arbitrary.
Note that WeakMap weak references only key names, not key values. The key value is still a normal reference.
In WeakMap, each key's reference to an object it references is a weak reference. Without other references and the key referencing the same object, the object will be garbage collected (the corresponding key becomes invalid), so the keys in WeakMap are not enumerable.
Attributes:
- Constructor: constructor
Method:
- has(key): Determine if there is a key associated object
- get(key): Returns the key associated object (undefined if none)
- set(key): Sets a set of key associated objects
- delete(key): Remove the associated object of the key
let myElement = document.getElementById('logo'); let myWeakmap = new WeakMap(); myWeakmap.set(myElement, {timesClicked: 0}); myElement.addEventListener('click', function() { let logoData = myWeakmap.get(myElement); logoData.timesClicked++; }, false);
WeakSet s are weak references in addition to WeakMap and can be recycled by the garbage collection mechanism to hold DOM nodes and are not prone to memory leaks
There's also the WeakRef for ES12, so if you're interested, it's too late this evening and we'll update it later
Reference resources
Last
Started from "Three Minute Front End", this article replies "Communication" to join the front-end three-minute advanced group automatically, and helps you to become a better front-end development by programming algorithm interviews (with answers) once a day!