What is a memory leak
A memory leak can be defined as a block of memory that is no longer used or needed by a program, but that for some reason has not been released and is still occupied unnecessarily. Creating objects and variables in code takes up memory, but javaScript has its own memory reclamation mechanism that determines which variables are no longer needed and removes them. But when your code logic defects, do you think you already don’t need, but also exists in the program reference, cause the program to run after no suitable recycling occupied space, cause memory utilization, the more occupied the longer run, the resulting, poor performance, high latency, frequent collapse. Before we dive into memory leaks, we need to know a few things:
- Memory life cycle
- Memory management system
- Garbage collection algorithm
Memory life cycle
Regardless of the programming language, the memory life cycle is basically the same: article cover chart
- Allocate as much memory as you need
- Use allocated memory (read, write)
- Release \ return it when it is not needed
All languages are explicit in part II. The first and third parts are explicit in low-level languages, but most of them are implicit in high-level languages like JavaScript.
Memory management system: manual or automatic
Different languages deal with their memory in different ways.
- Low-level languages: Low-level languages like C typically have low-level memory management interfaces, such as malloc() and free().
- High level languages: JavaScript automatically allocates memory when variables (objects, strings, etc.) are created and “automatically” frees them when they are not in use. The process of release is called garbage collection. This “automation” is a source of confusion and gives JavaScript (and other high-level languages) developers the false sense that they can afford not to care about memory management.
Garbage collection algorithm
Garbage collection periodically checks which previously allocated memory is “still needed” (×) which previously allocated memory is “still accessible to other parts of the program” (√).
Huh? A look of confusion…
This is the key to understanding garbage collection. Only the developer knows if this chunk of memory will be “needed” for future use, but algorithms can be used to determine inaccessible memory and mark it back to the operating system.
- Reference counting
- Mark clear
1. Reference count
This is the most rudimentary garbage collection algorithm; if there are no references to the object (zero references), the object will be collected by the garbage collection mechanism. (Example above MDN)
var o = { a: { b:2 } }; // Two objects are created, one is referenced as an attribute of the other, and the other is assigned to the variable o. // the o2 variable is the second reference to "this object" o = 1; // Now the original reference o to "this object" is replaced by o2. O_2 = "yo"; o_2 = "yo"; // The original object is now zero-referenced // it can be garbage collected // However, the object of its attribute A is still referenced by OA, so oa = null cannot be collected yet; // The object with the a attribute now has zero references // it can be garbage collectedCopy the code
Disadvantages: In the case of loops, the reference counting algorithm is very limited.
Function foo () {var obj1 = {}; var obj2 = {}; obj1.x = obj2 ; // obj1 references obj2 obj2.x = obj1; // obj2 references obj1 return true; } foo ();Copy the code
2. Mark clearing
The algorithm consists of the following steps:
- The garbage collector creates a list of roots. Roots are usually references to global variables in code. In JavaScript, the “window” object is a global variable and is treated as root. The Window object always exists, so the garbage collector can check that it and all of its children are present (that is, not garbage);
- All roots were checked and marked as active (i.e. not garbage). All child objects are also checked recursively. All objects from root onwards are not treated as garbage if they are reachable.
- Any unmarked memory is treated as garbage, and the collector can now free the memory and return it to the operating system.
No picture ()
The circular reference problem is solved. While the algorithm is constantly improving,js garbage collection (generated/incremental/concurrent garbage collection) the essence of these improvements remains the same: reachable memory is marked and the rest is treated as garbage collection.
Disadvantages: Algorithm runtime program execution is paused.
Garbage collection mechanisms are unpredictable
While garbage collection is nice and convenient, it’s a trade-off. The reason is that we cannot be sure when the collection will be performed. Only the developer can determine whether a chunk of memory can be returned to the operating system. An unwanted reference is when the developer knows that a memory reference is no longer needed, but for some reason it remains in the active root tree when we write the program.
How to avoid
The main cause of garbage collection language leakage is unwanted references. To understand what an unwanted reference is, we first need to understand how the garbage collector determines whether a piece of memory can be accessed.
So to understand which are the most common leaks in JavaScript, we need to know the way references are often forgotten.
There are four common types of memory leaks
1. Global variables
When referencing an undeclared variable in non-strict mode, a new variable is created in the global object. In the browser, the global object will be window, which means
Function foo (arg) {bar = "some text"; // bar will leak globally.}Copy the code
Why not leak to global, we usually define global variables!!
** Cause ** : Global variables are by definition not collected by garbage collection. Special attention needs to be paid to global variables used to temporarily store and process large amounts of information. If you must use a global variable to store data, be sure to specify it as NULL or reassign it when you are done. ** Solution **: Strict mode
2. Forgotten timers and callbacks
var someResource = getData(); setInterval(function() { var node = document.getElementById('Node'); if(node) { node.innerHTML = JSON.stringify(someResource)); } // node, someResource;}, 1000);Copy the code
Reason: Timers associated with nodes or data are no longer needed, node objects can be deleted, and the entire callback function is no longer needed. However, the timer callback function is still not recycled (it is not recycled until the timer stops). Also, someResource can’t be recycled if it stores a lot of data.
Solution: Manually clear the timer when the timer is finished
3. The DOM references
var refA = document.getElementById('refA'); document.body.removeChild(refA); // dom deletes console.log(refA, "refA"); // But there are references that can console out the entire div that are not reclaimedCopy the code
Cause: The REFERENCE to the DOM node is preserved, so the GC does not collect it
RefA = null;
Note: There are also references within the DOM tree or to child nodes to consider. Suppose your JavaScript code saves a reference to a < TD > in the table. When the decision is made to delete the entire table in the future, it is intuitive that the GC will reclaim any nodes other than the saved < TD >. This is not the case: the < TD > is a child node of the table, and the child element is referenced to the parent element. Because the code keeps a reference to < TD >, the entire table remains in memory. Be careful when saving references to DOM elements.
4. The closure
Pay attention to
Pay attention to
Note: Closures are not inherently wrong and do not cause memory leaks. It’s the use of errors.
var theThing = null; var replaceThing = function () { var originalThing = theThing; var unused = function () { if (originalThing) console.log("hi"); }; theThing = { longStr: new Array(1000000).join('*'), someMethod: function () { console.log(someMessage); }}; }; setInterval(replaceThing, 1000);Copy the code
This is bad code, and every time replaceThing is called, theThing gets a new object that contains a large array and a new closure (someMethod). Unused, meanwhile, is a closure that refers to originalThing (the previous replaceThing called theThing). Confused? The most important thing is that once the scope of a closure is created, they have the same parent scope, and the scope is shared. SomeMethod can be used by theThing. SomeMethod shares closure scope with unused, although unused is never used, and its reference to originalThing forces it to remain in memory (to prevent it from being reclaimed). As this code is run repeatedly, you will see the memory footprint increase and the garbage collector (GC) will not be able to reduce it. In essence, a linked list of closures has been created, and each closure scope carries an indirect reference to a large array, causing a serious memory leak.
Solution: Remove unuserd or add originlThing = null to the last line of replaceThing.
Reference:
4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them
How JavaScript works: memory management + how to handle 4 common memory leaks