foreword
Hello everyone, I'm Lin Sanxin. In the last article, I told you that gave you 13 pictures, which helped you defeat the "V8 garbage collection mechanism" 20 minutes, but Guan knows that the recycling mechanism is not good, V8 garbage collection The mechanism is very strong, but we can’t just create a lot of garbage for recycling. We have to reduce the amount of garbage as much as possible during development. Today, I will tell you how to avoid too much garbage in
why avoid
What is the memory leak? That is, some garbage that should be recycled is not recycled, which causes the accumulation of garbage.
memory leak sounds far away, but it is actually very close to us, and we have been in contact with it directly or indirectly. For example, sometimes your page gets stuck when you use it, and it gets more and more stuck as time
by.
this time, you need to consider whether it is a 161e36e192494b memory leak problem. 161e36e192494d memory leak affects the user experience. It is a major problem, so it is very necessary to avoid it through correct coding habits.
How to Monitor Memory Status
We have been emphasizing memory and memory, but I feel that it is a very illusory thing, at least let us see its true colors, right?
Browser Task Manager
Open: In the top of the browser right, open
Task Manager:
After opening, we see memory and
JavaScript memory (in parentheses):
memory: the original memory in the page, that is, the total memory occupied by
DOM nodes
JavaScript memory (in parentheses): is the total memory occupied
reachable objects in the page
So what is a reachable object? Simply put: it starts from
root object (window or global), and searches down the child node. If the child node is searched, it means that the reference object of the child node is reachable. If it cannot be found, it means the child node. Object is not reachable. for example:
// 可达,可以通过window.name访问
var name = '林三心'
function fn () {
// 不可达,访问不了
var name = '林三心'
}
Back to our task management, at this time we write a piece of code in the page:
<button id="btn">点击</button>
<script>
document.getElementById('btn').onclick = function () {
list = new Array(1000000)
}
</script>
Before clicking:
After clicking, it is found that the memory rises instantly:
Performance
Using the incognito mode of the Chrome browser is to avoid many other factors that affect our view of memory:
Press F12 to open the debug window and select Performance
Let's take the Nuggets homepage as an example! Click Record -> Refresh Nuggets -> Click stop , you can see the following indicators fluctuate up and down :
JS Heap
: JS heapDocuments
: DocumentationNodes
: DOM nodeListeners
: ListenerGPU Memory
: GPU memory
heap snapshot
heap snapshot, as the name suggests, is to take a picture
heap memory of a current page and save it. For the same page, before performing an operation, the recorded heap snapshot is the same. It is possible that after the execution, the recorded heap snapshot will be different. It's another kind.
Or take the Nuggets homepage as an example, you can see that the current page memory is
13.3M
, we can choose Statistics
, and view the memory occupied by arrays, objects, strings, etc.
how to avoid
As mentioned above, in fact, the memory leak problem is very close to us, and we may have caused it directly or indirectly. Next, let's talk about how to avoid this problem, it may also be a bad habit in your development!
reduce global variables
We may have encountered such code in development. In fact, we just want to use a as a local variable, but forgot to write var,let,const
:
document.getElementById('btn').onclick = function () {
// a 未在外部声明过
a = new Array(1000000).fill('Sunshine_Lin')
}
上方代码等同于
var a
document.getElementById('btn').onclick = function () {
a = new Array(1000000).fill('Sunshine_Lin')
}
What's the downside? We mentioned reachability earlier, which can be explained here. If the above code is written like this, we can access the
global variable
a
window.a
, so a is reachable, and it will not be collected as garbage, which will cause it to keep occupying memory and not be released, consuming performance , contrary to our original intention. We can heap snapshot. The steps are:
record -> click the button -> record, compare the results twice, after clicking, the memory is larger by
4M
, check Statistics
, and find that the array memory is much larger and has not been released :
How should it be improved? You can add the define variable operator:
document.getElementById('btn').onclick = function () {
let a = new Array(1000000).fill('Sunshine_Lin')
}
Looking at the effect, due to local variables, is unreachable. Every time a function is executed, it will be reclaimed
and released, so it will not occupy the memory all the time. The memory before and after the click is similar:
Timer not cleared
Please look at this piece of code. In this code, after executing the fn1 function, it stands to reason that the arr array will be recycled, but it cannot be recycled. why? Because a in the timer refers to arr, and if the timer is not cleared, a
will not be recycled. a
not recycled, it will always refer to arr
, so arr
definitely not be recycled.
function fn() {
let arr = new Array(1000000).fill('Sunshine_Lin')
setInterval(() => {
let a = arr
}, 1000)
}
document.getElementById('btn').onclick = function () {
fn()
}
Performace: Recording -> Manual Garbage Collection -> Click the button five times in a row -> Manual Garbage Collection -> End
The first and last two manual garbage collections are to compare the lowest points of garbage memory in the first and last two times. If there is no memory leak problem, the two lowest points should be the same. Here you can see that the tail is more than the head. The part, is the amount of memory that has not been reclaimed
As mentioned above, why is the arr array not recycled? It is because the
timer is not cleared, so that
a
keeps referencing arr
. How to solve it? clear the 161e36e192510d timer directly.
function fn() {
let arr = new Array(1000000).fill('Sunshine_Lin')
let i = 0
let timer = setInterval(() => {
if (i > 5) clearInterval(timer)
let a = arr
i++
}, 1000)
}
document.getElementById('btn').onclick = function () {
fn()
}
Look at Performance
, and find that the amount of memory in the first two times is the same, which means that it is normal
Use closures wisely
Let's look at this piece of code:
function fn1() {
let arr = new Array(100000).fill('Sunshine_Lin')
return arr
}
let a = []
document.getElementById('btn').onclick = function () {
a.push(fn1())
}
It stands to reason that fn1
executed, arr
will be recycled, but in this code, it is not recycled, why? Because fn1
After execution, the arr
to return
out, then arr
is into a array of push, but the array is a global variable,
a array will not be recovered, then
a natural array of things will not be Recycling, this will lead to
arr
will not be recycled. After more and more clicks, there will be more and more arr
a
not used later, then these arr
will become useless garbage, let's This can be verified with Performance
and heap snapshots:
Performace: Recording -> Manual Garbage Collection -> Click the button five times in a row -> Manual Garbage Collection -> End
The first and last two manual garbage collections are to compare the lowest points of garbage memory in the first and last two times. If there is no memory leak problem, the two lowest points should be the same. Here you can see that the tail is more than the head. The part, is the amount of memory that has not been reclaimed
heap snapshot: first recording -> click the button 5 times in a row -> second recording
You will find that before and after the click, there is a lot more memory, and the extra memory is the amount of memory that has not been reclaimed.
Detach the DOM
What is the separation DOM? Or use code to speak:
<button id="btn">点击</button>
let btn = document.getElementById('btn')
document.body.removeChild(btn)
Although the button was finally deleted, because the global variable btn
to the DOM object, the
DOM
object has not been recycled. This DOM object is called the
detached DOM. We can verify this
heap snapshot. Problem, search for
detached (Chinese meaning: independent, detached) in the heap snapshot:
This problem is easy to solve. After deleting the button, set the btn to null
by the way:
<button id="btn">点击</button>
let btn = document.getElementById('btn')
document.body.removeChild(btn)
btn = null
At this time, the button DOM is really completely erased from js:
References
- Taobao’s front-end optimized? How to write JavaScript efficiently? What are some tricks to improve JS performance?
- you how to troubleshoot page jams caused by memory leaks.
Epilogue
I'm Lin Sanxin, an enthusiastic front-end rookie programmer. If you are motivated, like the front-end, and want to learn the front-end, then we can make friends and fish together haha, touch the fish group, add me, please note [Si No]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。