程序的運行需要內存。只要程序提出要求,操作系統或者運行時就必須供給內存。對于持續運行的服務進程,必須及時釋放不再用到的內存。否則,內存占用越來越高,輕則影響系統性能,重則導致進程崩潰。不再用到的內存,沒有及時釋放,就是內存泄漏(memory leak)。而內存溢出(out of menory),指的是程序需求的內存,超出了系統所能分配的范圍。
一、內存泄漏(memory leak)
內存泄漏是每個開發者最終都要面對的問題,它是許多問題的根源:反應遲緩,崩潰,高延遲,以及其他應用問題。
1. 概念
應用程序不再需要占用內存的時候,由于某些原因,內存沒有被操作系統或可用內存池回收。
2. 常見內存泄漏
意外的全局變量JS中對于未聲明的變量,會在全局范圍中創建一個新的變量來對其進行引用。在瀏覽器中,全局對象是window。
function fn() {
a = 3
console.log(a)
}
fn()
沒有及時清理的循環計時器
var intervalId = setInterval(function () {
console.log( '----')
}, 1000)
//未清除當前循環定時器 cLearInterval(intervaLId)
閉包未釋放閉包(closure)是一個函數以及其捆綁的周邊環境狀態(lexical environment,詞法環境)的引用的組合。換而言之,閉包讓開發者可以從內部函數訪問外部函數的作用域。在 JAVAScript 中,閉包會隨著函數的創建而被同時創建。當閉包被使用,內部函數使用了外部函數的變量,就會造成內存泄漏。
function fn1() {
var a = 1
function fn2() {
var b = ++a
console.log(b)
}
return fn2
}
var f = fn1()
f()
//釋放閉包 f = null
3. 怎樣避免內存泄漏
減少不必要的全局變量,或者生命周期較長的對象,及時對無用的數據進行垃圾回收;
注意程序邏輯,避免“死循環”之類的;
避免創建過多的對象 原則:不用了的東西要及時歸還。
二、垃圾回收機制
因為不是所有無用對象內存都可以被垃圾回收機制回收的,那當不再用到的內存,沒有及時回收時,我們叫內存泄漏。那么我們接下來了解下垃圾回收機制。
1.垃圾如何產生?又如何判斷垃圾?
簡單來說垃圾就是程序不用的內存或者是之前用過了,以后不會再用的內存空間。如何判斷垃圾就是看這個對象能否被訪問,那如何知道對象能否被訪問?有一個專業的詞叫可達性。根據對象是否可達來判斷??蛇_就不需要被回收,不可達就需要被回收。
let obj= {name: 'zs'}
obj = [1, 2, 3]
在js中數據分基本數據類型和引用數據類型,引用數據類型在棧中保存的是引用,實際是存儲在堆中的。在上面的例子中我們首先創建了一個obj變量指向對象{name: 'zs'},然后又把obj指向了新的數組[1, 2, 3],所以之前的{name: 'zs'}就不可能被訪問到了(沒有了可達性),就變成了垃圾。
2.為什么要垃圾回收?
從上面的例子可以看出產生了垃圾就會導致浪費內存空間,一個兩個還好,多了的話我們的程序可能會越來越卡頓,到最后崩潰。所以就需要垃圾回收機制來幫我們自動清理沒用的垃圾,釋放出更多的內存來給當前程序使用,這樣程序就會一直流暢的運行下去。
3.垃圾回收的方式
垃圾回收機制里面最常用的兩個方式就是標記清除法和引用計數法。
標記清除法這是目前瀏覽器大多基于標記清除法。我們可以分為兩個階段:標記:從根節點遍歷為每個可以訪問到的對象都打上一個標記,表示該對象可達。清除:在沒有可用分塊時,對堆內存遍歷,若沒有被標記為可達對象就將其回收。
引用計數法引用計數法就是追蹤每個變量被引用的次數,當引用數為0將可以被回收掉。
三、內存溢出(out of menory)
一種程序運行出現的錯誤,當程序運行需要的內存超過剩余內存時就會拋出內存溢出的錯誤。
1.概念
程序要求的內存,超出了系統所能分配的范圍。
2.舉例
var arr=[]
for (let i = 0; i < 1000000; i++) {
arr[i] = new Array(1000000)
}
//網頁直接崩潰,錯誤代碼:Out of Memory
四、實際項目中出現的內存溢出
VUE項目開發時間長了,隨著項目越來越大,打包的時間也相應的變長,打包時的內存也增多了。這時候產生了一個問題,在發布項目的時候,會出現類似如下錯誤的提示。
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation fAIled - JavaScript heap out of memory
out of memory內存溢出,使用內存時只能使用部分內存(64位系統:1.4 GB,32位系統:0.7 GB),這個時候,如果前端項目非常的龐大,Webpack編譯時就會占用很多的系統資源,如果超出了V8引擎對默認的內存限制大小時,就會產生內存溢出的錯誤。
解決辦法如下:
// 安裝兩個npm包 :cross-env increase-memory-limit
// npm install cross-env increase-memory-limit
// 再package.json中添加fix-memory-limit命令
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"fix-memory-limit": "cross-env LIMIT=4096 increase-memory-limit",
},
/*
操作完以上步驟之后,可能會報錯 “node –max-old-space-size=4096不是內部或外部命令``”該問題的解決辦法:
在項目的node_modules.bin下面找到所有的*.cmd文件,
在ENDLOCAL語句的上邊一行,修改"%_prog%" 改為 %_prog%, 去掉雙引號。
*/
// LIMIT是你想分配的內存大小,然后執行npm run fix-memory-limit。
再執行npm run serve重新啟動項目,就不會再內存溢出了。
五、總結
內存泄漏積累過多最終會導致內存溢出,因為系統中的內存是有限的,如果過度占用資源而不及時釋放,最后會導致內存不足,從而無法給所需要存儲的數據提供足夠的內存,從而導致內存溢出。