內存泄露(Memory Leak)指因爲疏忽或錯誤形成程序未能釋放已經再也不使用內存的狀況。若是內存泄露的位置比較關鍵,那麼隨着處理的進行可能持有愈來愈多的無用內存,這些無用內存的變多會引發服務器響應速度變慢,嚴重的狀況下致使內存達到某個極限(多是進程的上限,如V8的上限;也多是系統可提供的內存上限)會使得應用程序奔潰。
傳統的 C/C++ 中存在指針,對象在用完以後未釋放等狀況致使的內存泄露。而在使用虛擬機執行的語言中如 Java、Javascript 因爲使用了GC(Garbage Collection,垃圾回收)機制自動釋放內存,使得程序員的精力獲得了極大的解放,不用再像傳統語言那樣時刻對於內存的釋放而戰戰兢兢。
可是,即便有了GC機制能夠自動釋放,但這並不意味着內存泄露的問題不存在了。內存泄露依舊是開發者們不能繞過的一個問題。javascript
Node.js使用V8做爲Javascript的執行引擎,因此討論Node.js的GC狀況就等同於在討論V8的GC。在V8中一個對象的內存釋放被釋放,是看程序中是否還有地方持有該對象的引用。
在V8中,每次GC時,是根據root對象(瀏覽器環境下的window,Node.js環境下的global)依次梳理對象的引用,若是能從root的引用鏈到達訪問,V8就會將其標記爲可到達對象,反之爲不可到達對象。被標記爲不可到達對象(即無引用的對象)後就會被V8回收。
在NodeJS中內存泄露的緣由就是:本該被清除的對象,被可到達對象引用以後,未被正確的清除而常駐內存。java
a = 10; // 未聲明對象
global.b = 11; // 全局變量引用
複製代碼
這種緣由比較簡單,全局變量直接掛在 root 對象上,不會被清除掉。程序員
function out(){
const data = Buffer.alloc(100);
inner = function(){
void data
}
}
複製代碼
閉包會引用到父級函數中的變量,若是閉包未釋放,就會致使內存泄露。
這裏的例子只是簡單的將引用掛在全局對象上,實際的業務狀況可呢呢個是掛在某個能夠從root追溯到的對象上致使的。瀏覽器
NodeJS的事件監聽也可能出現內存泄露。例如對同一個事件重複監聽,忘記移除(removeListener),將形成內存泄露。這種狀況很容易在複用對象上添加事件時出現。
例如,NodeJS中Agent的keepAlive爲true時,可能形成內存的泄露。當Agent keepAlive爲true時,將會複用以前使用過的socket,若是在socket上添加事件監聽,忘記清除的話,由於socket的複用,將致使事件重複監遵從而致使內存泄露。
原理上與添加事件監聽忘了清除是同樣的。在使用NodeJS的http模塊時,不經過keepAlive複用是沒有問題的,複用了之後就可能產生內存泄露。因此你須要瞭解添加事件監聽對象的生命週期,並注意自行移除。緩存
還有一些其餘的狀況可能會致使內存泄露,好比緩存。在使用緩存的時候,得清楚緩存的對象是多少,若是緩存對象很是多,得作限制最大緩存數量處理。還有就是很是佔用CPU的代碼也會致使內存泄露,服務器在運行的時候,若是有高CPU的同步代碼,由於NodeJS是單線程的,因此就不能處理後面的請求了,請求堆積致使內存佔用太高。服務器