A memory leak refers to the situation where dynamically allocated heap memory in a program is not released due to negligence or an error, resulting in wastage of system memory, slow program execution, or even system crashes. A memory leak does not necessarily mean that the memory physically disappears, but rather that the application allocates a segment of memory and, due to a design error, loses control over that segment before releasing it, thereby causing memory wastage. To detect memory leaks, Chrome provides performance analysis tools such as "Performance," which allow for convenient monitoring of memory usage.
Low-level languages like C generally have low-level memory management interfaces, such as malloc()
and free()
. In JavaScript, memory is automatically allocated when variables are created and automatically released when they are no longer in use. Among the seven basic types in JavaScript, objects, which are reference types, occupy a large and variable-sized memory space in the heap and have actual object storage in the heap memory and pointer storage in the stack memory. Access to objects is done through reference. Variables in the stack area are accessed by value and are destroyed when their scope is terminated. However, heap variables accessed by reference may still be referenced in an outer scope or another scope even after their original scope is terminated. They cannot be directly destroyed. In such cases, algorithms are used to determine whether the heap variable is no longer needed, thus deciding whether memory reclamation is necessary. JavaScript mainly uses two garbage collection algorithms: reference counting and mark-and-sweep.
In the reference counting garbage collection algorithm, whether an object is no longer needed is simplified to whether other variables or objects reference it. If there are no references pointing to an object, the garbage collector reclaims it. Here, an object refers not only to JavaScript objects, but also to function scope or global lexical scope. The reference counting garbage collection algorithm is used less frequently and is mainly used in older versions of Internet Explorer, such as IE6 and IE7.
However, the reference counting garbage collection algorithm has a limitation: it cannot handle memory leaks caused by circular references.
In the mark-and-sweep garbage collection algorithm, whether an object is no longer needed is defined as whether the object can be reached. This algorithm sets a root object, which in JavaScript is the global object. The garbage collector periodically starts from the root and finds all objects referenced from it, and then recursively finds objects referenced by those objects. Starting from the root, the garbage collector identifies reachable and unreachable objects. This effectively solves the problem of circular references. All modern browsers use the mark-and-sweep garbage collection algorithm, and any improvements made to JavaScript garbage collection algorithms are based on this approach.
In JavaScript
, the handling of undeclared variables is not strictly defined. Even within a local function scope, global variables can still be defined. These unexpected global variables may store a large amount of data and, because they can be accessed through global objects such as window
, they are not considered for memory collection during garbage collection. They persist until the window is closed or the page is refreshed, causing unexpected memory leaks. In strict mode, this unexpected global variable declaration will throw an exception in JavaScript
. Furthermore, you can use eslint
to perform prechecks for this kind of state. In fact, defining global variables is not a good practice. If you must use global variables to store a large amount of data, make sure to set it to null
or redefine it after use. One of the main reasons for increased memory consumption related to global variables is caching. Cached data is meant for reuse and must have a size limit to be useful. High memory consumption leads to the cache exceeding its limit because the cached content cannot be reclaimed.
Timers like setInterval
must be cleared in a timely manner, as otherwise the variables or functions referenced within may not be collected due to being considered necessary. If the internally referenced variables store a large amount of data, it can lead to excessive memory usage, thus causing unexpected memory leaks.
Sometimes, it is useful to save the internal data structure of a DOM
node, such as when you need to quickly update the content of several rows in a table, storing each row DOM
as a dictionary or array makes sense. In this case, the same DOM
element exists in two references: one in the DOM
tree and another in the dictionary. In the future, if you decide to delete these rows, you need to clear both references. Additionally, you should also consider the issue of references within the DOM
tree or its child nodes. If your JavaScript
code saves a reference to a specific <td>
in a table, and you later decide to delete the entire table, your intuition might lead you to believe that the GC will reclaim all nodes except the saved <td>
. The actual situation is different. This <td>
is a child node of the table, and due to the code retaining a reference to the <td>
, the entire table remains in memory. Therefore, when saving references to DOM
elements, caution is advised.
Closures are a key aspect of JavaScript
development. They allow you to access the outer function scope from an inner function, essentially enabling access to another function's scope without necessarily implementing a scope chain structure within the function scope. Since closures carry the scope of the function that contains them, they can consume more memory than other functions. Overusing closures may lead to excessive memory consumption, and they need to be manually cleared after they are no longer needed.
When implementing the listener pattern and mounting relevant event handling functions within a component, if these are not actively cleared upon the component's destruction, the referenced variables or functions are considered necessary and will not be recycled. If the internally referenced variables store a large amount of data, it may lead to excessive memory usage and unexpected memory leaks.
When event listeners mount relevant event handling functions within a component, but are not actively cleared upon the component's destruction, the referenced variables or functions are considered necessary and will not be recycled. If the internally referenced variables store a large amount of data, it may lead to excessive memory usage and unexpected memory leaks.
When using Map
to store objects, similar to detaching references from the DOM
, if the references are not actively cleared, it will also lead to the memory not being automatically reclaimed. In cases where the key is an object, WeakMap
can be used. WeakMap
is used to save key-value pairs, with the key being a weak reference and must be an object, while the value can be any object or primitive value. Since it is a weak reference to the object, it does not interfere with JavaScript garbage collection.
When using Set
to store objects, similar to detaching references from the DOM
, if the references are not actively cleared, it will also lead to the memory not being automatically reclaimed. If we need to reference objects using Set
, WeakSet
can be used. WeakSet
allows storing the unique values of weak references to objects, and values in the WeakSet
cannot be repeated. It can only store weak references to objects and does not interfere with JavaScript garbage collection.