一、內存生命週期
- 內存分配:當我們申明變量、函數、對象的時候,系統會自動爲他們分配內存
- 內存使用:即讀寫內存,也就是使用變量、函數等
- 內存回收:使用完畢,由垃圾回收機制自動回收不再使用的內存
二、內存泄漏
內存泄漏:不再用到的內存,沒有及時釋放,就叫做內存泄漏(memory leak)。
三、檢測內容泄漏的方法
1.通過谷歌瀏覽器檢測:
打開chrome檢查,點擊Performance,勾選Memory,點擊左上角的Record小圓點,開始錄製
錄製時進行一些用戶基本操作,操作結束後點擊stop按鈕,結束錄製,頁面會呈現錄製結果
結果:
判定當前是否有內存泄漏
- 多次快照後,比較每次快照中內存的佔用情況,如果呈上升趨勢,那麼可以認爲存在內存泄漏
- 某次快照後,看當前內存佔用的趨勢圖,如果走勢不平穩,呈上升趨勢,那麼可以認爲存在內存泄漏
- 經過一些反覆測試後,你看到的是鋸齒狀的圖形(有上有下),說明你的程序中有很多短時存在的對象。
只看JS Heap(堆內存)這條線:
2.通過node.js的process. memoryUsage()檢測
console.log(process.memoryUsage());
// {
// rss: 27709440,
// heapTotal: 5685248,
// heapUsed: 3449392,
// external: 8772
// }
- rss(resident set size):所有內存佔用,包括指令區和堆棧。
- heapTotal:"堆"佔用的內存,包括用到的和沒用到的。
- heapUsed:用到的堆的部分。
- external: V8 引擎內部的 C++ 對象佔用的內存。
四、可能導致內存泄漏的案例
1.聲明時內存泄漏
function fn() {
a = 1;
}
fn();
console.log(a); // 1
解決:聲明時使用var、let、const.
function fn() {
let a = 1;
}
fn();
console.log(a); // a is not defined
2.this導致內存泄漏
function fn() {
this.a = 1; // this指向全局對象window,導致內存泄漏
}
fn();
console.log(this.a); // 1
3.沒清除setInterval導致內存泄漏
setInterval沒有清除,僅僅清除dom元素,定時器依舊不斷工作
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div>
<p id="text"></p>
<button id="btn">移除</button>
</div>
<script>
window.onload = function() {
let p = document.getElementById('text');
setInterval(function() {
let time = new Date();
p.innerHTML = JSON.stringify(time);
console.log(111);
}, 1000);
let btn = document.getElementById('btn');
btn.onclick = function() {
p.remove();
}
}
</script>
</body>
</html>
解決:刪除定時器
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div>
<p id="text"></p>
<button id="btn">移除</button>
</div>
<script>
window.onload = function() {
let timer = null;
let p = document.getElementById('text');
timer = setInterval(function() {
let time = new Date();
p.innerHTML = JSON.stringify(time);
console.log(111);
}, 1000);
let btn = document.getElementById('btn');
btn.onclick = function() {
p.remove();
clearInterval(timer);
}
}
</script>
</body>
</html>
4.閉包導致內存泄漏
function Person() {
this.name = 'John';
this.age = 12;
}
Person.prototype.showName = function() {
let _this = this; //this常駐於內存中,閉包裏的變量會持續保存,沒有清理就會一直佔用內存
return function() {
return _this.name;
}
}
解決方法:保存this的屬性,用完要置空。
function Person() {
this.name = 'John';
this.age = 12;
}
Person.prototype.showName = function() {
let name= this.name; //this常駐於內存中,閉包裏的變量會持續保存,沒有清理就會一直佔用內存
return function() {
return name;
}
name = null;
}