preface
This chapter introduces you to the knowledge point is relatively simple, but it is very important. It’s also part of the interview process.
You can learn from this reading:
- There are four common memory leaks
- Identification of memory leaks
There are four common memory leaks
In practice, it’s easy to inadvertently write code that leaks memory, as you might have seen in the following cases.
1. Unexpected global variables
Undeclared variables
When we assign a value to a variable in a function but do not declare it:
function fn () {
a = "Actually, I'm a global variable"
}
Copy the code
The variable a is equivalent to a variable in the window object:
function fn () {
window.a = "Actually, I'm a global variable"
}
Copy the code
We have already stated that global variables are very difficult to collect by the garbage collector, so unexpected global variables should be avoided.
usethis
Created variable
Here’s another case:
function fn () {
this.a = "Actually, I'm a global variable"
}
fn();
Copy the code
We know that this refers to window, so the a variable created at this point will also be mounted under the Window object.
The way to avoid this is to add ‘use strict’ to the top of your JavaScript file header or function, turn on strict mode, and make this point undefined.
Of course, if you have to use a global variable to store a lot of data, be sure to set it to NULL or redefine it when you’re done.
“
Forgotten timers or callback functions
Timer cause
We can also leak memory when using timers in code:
var serverData = loadData()
setInterval(function() {
var renderer = document.getElementById('renderer')
if(renderer) {
renderer.innerHTML = JSON.stringify(serverData)
}
}, 5000)
Copy the code
In the above example 🌰, the node renderer references serverData. The timer still points to the data when the node renderer or data is no longer needed. So even when the Renderer node is removed, the Interval still lives and the garbage collector cannot collect, nor can its dependencies be collected, unless the timer is terminated.
Object observer
Another example is the observer model:
var btn = document.getElementById('btn');
function onClick (element) {
element.innerHTMl = "I'm innerHTML"
}
btn.addEventListener('click', onClick);
Copy the code
In the case of the observers above, it is important to explicitly remove them once they are no longer needed (or the associated objects become unreachable). Old IE 6 couldn’t handle circular references. Older versions of IE were unable to detect cyclic references between DOM nodes and JavaScript code, leading to memory leaks.
However, modern browsers (including Internet Explorer and Microsoft Edge) use more advanced garbage collection algorithms (tag sweep) that already detect and handle circular references correctly. You don’t have to call removeEventListener to reclaim node memory.
3. Out-of-dom references
The cause of this memory leak is simply:
If you store the DOM as a dictionary (JSON key-value pairs) or an array, the same DOM element has two references: one in the DOM tree and one in the dictionary. Both references will need to be cleared in the future.
Take this example:
// Reference the DOM in the object var elements = { btn: document.getElementById('btn')}
function doSomeThing () { elements.btn.click(); }
Copy the code
function removeBtn () { // Remove the BTN from the body, i.e. remove the BTN from the DOM tree document.body.removeChild(document.getElementById('button')); // At this point, however, the global variable elements retains a reference to BTN, which is still in memory and cannot be collected by GC }
In the 👆 case above, you can manually clear the reference to elements. BTN = null.
Fourth, the closure
And then there’s the closure we talked about earlier. This is what happens to closures when local variables are destroyed.
First, let’s be clear: the key to closures is that anonymous functions can access variables in the parent scope.
A simple example 🌰:
function fn () {
var a = "I'm a";
return function () {
console.log(a);
};
}
Copy the code
Because variable a is referenced by an anonymous function inside the fn() function, this variable is not recycled.
So one might ask, even if that would be wrong? In the above case 👆, of course, nothing is apparent. What if you make it more complicated?
var globalVar = null; // Global variables
var fn = function () {
var originVal = globalVar; // Local variables
var unused = function () { // Unused function
if (originVal) {
console.log('call')
}
}
globalVar = {
longStr: new Array(1000000).join(The '*'),
someThing: function () {
console.log('someThing')
}
}
}
setInterval(fn, 100);
Copy the code
Take a minute to look at the example above and you will find:
- Each call
fn
Function to create a new objectoriginVal
; - variable
unused
It’s a quoteoriginVal
The closure; unused
It’s not used, but it’s referencedoriginVal
Force it to stay in memory and not be reclaimed.
The solution is to set originVal to NULL at the very bottom of fn.
Identification of memory leaks
The above 👆 introduces so many possible memory leaks, so is there any practical way to see the performance of memory leaks?
Of course there are. The following two ways are commonly used now:
Chrome
Browser consolePerformance
orMemory
Node
To provide theprocess.memoryUsage
methods
Chrome
Browser consolePerformance
orMemory
To check the memory usage on Chrome, perform the following steps.
- Right-click on the page and click “Check” to open the console (
Mac
shortcutsoption+command+i
); - choose
Performance
Panel (used in many textbooksTimeline
Panel, I don’t know if the version is not the reason); - Check the
Memory
Then click on the black dot in the upper left cornerRecord
Start recording; - Click in the popover
Stop
After recording, the panel displays the memory usage during this period.
If memory usage keeps incrementing, it is a memory leak:
Or you can use the method I described in Documenting Memory Leaks caused by One-time Timer and Closure Problems.
Node
To provide theprocess.memoryUsage
methods
The other is the process.memoryUsage method provided by Node. I don’t use this method very much.
console.log(process.memoryUsage());
// { rss: 27709440,
// heapTotal: 5685248,
// heapUsed: 3449392,
// external: 8772 }
Copy the code
Process. memoryUsage returns an object containing memoryUsage information for Node processes. This object contains four fields, in bytes, with the following meanings:
Resident set size (RSS) : All memory usage, including instruction area and stack. HeapTotal: The amount of memory occupied by the heap, both used and unused. HeapUsed: part of the heapUsed. External: memory occupied by C++ objects inside the V8 engine.Copy the code
The heapUsed field is used to determine memory leaks.
conclusion
In general, common memory leaks include:
- Unexpected global variables
- Forgotten timer or callback function
- Out-of-dom references
- A variable created repeatedly in a closure
How to avoid memory leaks:
- Pay attention to program logic and avoid “endless loops” and the like
- Reduce unnecessary global variables, or objects with long life cycles, and timely garbage collection of useless data
- Rule of thumb to avoid creating too many objects: Return what you don’t use
After the language
That concludes the JavaScript advanced memory stack, which is a total of five articles. In the article I also try to use more popular language to explain, I hope everyone can understand.
If you like Lin silly article, but also please help me a small favor, pay attention to a wave of I recently just began to beat the public number, I will above the irregular hair some of the front end of the original article, learn together, progress together 😊.