對於持續運行的服務進程(daemon),必須及時釋放再也不用到的內存。不然,內存佔用愈來愈高,輕則影響系統性能,重則致使進程崩潰。 對於再也不用到的內存,沒有及時釋放,就叫作內存泄漏(memory leak)html
node.js中V8中的內存分代:node
啓動node進程時添加參數便可 node --max-old-space-size=1700 <project-name>.js 調整老生代內存限制,單位爲MB(貌似最高也只能1.8G的樣子)(老生代默認限制爲 64/32 位 => 1400/700 MB)git
node --max-new-space-size=1024 <project-name>.js 調整新生代內存限制,單位爲KB(老生代默認限制爲 64/32 位 => 32/16 MB) 接!github
內存回收時使用的算法:算法
Scavenge 算法(用於新生代,具體實現中採用 Cheney 算法)chrome
Mark-Sweep & Mark-Compact(用於老生代的回收算法)數組
buffer 聲明的都爲堆外內存,它們是由系統限定而非 V8 限定,直接由 C++ 進行垃圾回收處理,而不是 V8,在進行網絡流與文件 I/O 的處理時,buffer 明顯知足它們的業務需求,而直接處理字符串的方式,顯然在處理大文件時有心無力。因此由 V8 處理的都爲堆內內存。瀏覽器
一、瀏覽器方法緩存
二、命令行方法 使用 Node 提供的 process.memoryUsage 方法。網絡
console.log(process.memoryUsage()); // 輸出 { rss: 27709440, // resident set size,全部內存佔用,包括指令區和堆棧 heapTotal: 5685248, // "堆"佔用的內存,包括用到的和沒用到的 heapUsed: 3449392, // 用到的堆的部分 external: 8772 // V8 引擎內部的 C++ 對象佔用的內存 }
判斷內存泄漏,以heapUsed字段爲準。
意外的全局變量
function foo(arg) { bar = "this is a hidden global variable"; // winodw.bar = ... } 或者 function foo() { this.variable = "potential accidental global"; } // Foo 調用本身,this 指向了全局對象(window) // 而不是 undefined foo();
解決方法: 在 JavaScript 文件頭部加上 'use strict',使用嚴格模式避免意外的全局變量,此時上例中的this指向undefined。
儘管咱們討論了一些意外的全局變量,可是仍有一些明確的全局變量產生的垃圾。它們被定義爲不可回收(除非定義爲空或從新分配)。尤爲當全局變量用於臨時存儲和處理大量信息時,須要多加當心。若是必須使用全局變量存儲大量數據時,確保用完之後把它設置爲 null 或者從新定義。與全局變量相關的增長內存消耗的一個主因是緩存。緩存數據是爲了重用,緩存必須有一個大小上限纔有用。高內存消耗致使緩存突破上限,由於緩存內容沒法被回收。
被遺忘的計時器或回調函數
如計時器的使用:
var someResource = getData(); setInterval(function() { var node = document.getElementById('Node'); if(node) { // 處理 node 和 someResource node.innerHTML = JSON.stringify(someResource)); } }, 1000);
定義了一個someResource變量,變量在計時器setInterval內部一直被引用着,成爲一個閉包使用,即便移除了Node節點,因爲計時器setInterval沒有中止。其內部仍是有對someResource的引用,因此v8不會釋放someResource變量的。
var element = document.getElementById('button'); function onClick(event) { element.innerHTML = 'text'; } element.addEventListener('click', onClick);
對於上面觀察者的例子,一旦它們再也不須要(或者關聯的對象變成不可達),明確地移除它們很是重要。老的 IE 6 是沒法處理循環引用的。由於老版本的 IE 是沒法檢測 DOM 節點與 JavaScript 代碼之間的循環引用,會致使內存泄漏。
可是,現代的瀏覽器(包括 IE 和 Microsoft Edge)使用了更先進的垃圾回收算法(標記清除),已經能夠正確檢測和處理循環引用 了。即回收節點內存時,沒必要非要調用 removeEventListener 了。(不是很理解)
對DOM 的額外引用
若是把DOM 存成字典(JSON 鍵值對)或者數組,此時,同一個 DOM 元素存在兩個引用:一個在 DOM 樹中,另外一個在字典中。若是要回收該DOM元素內存,須要同時清除掉這兩個引用。
var elements = { button: document.getElementById('button'), image: document.getElementById('image'), text: document.getElementById('text') }; document.body.removeChild(document.getElementById('button')); // 此時,仍舊存在一個全局的 #button 的引用(在elements裏面)。button 元素仍舊在內存中,不能被回收。
若是代碼中保存了表格某一個 <td> 的引用。未來決定刪除整個表格的時候,咱們覺得 GC 會回收除了已保存的 <td> 之外的其它節點。實際狀況並不是如此:此 <td> 是表格的子節點,子元素與父元素是引用關係。因爲代碼保留了 <td> 的引用,致使整個表格仍待在內存中。
因此保存 DOM 元素引用的時候,要當心謹慎。
閉包
var theThing = null; var replaceThing = function () { var originalThing = theThing; var unused = function () { if (originalThing) console.log("hi"); }; theThing = { longStr: new Array(1000000).join('*'), someMethod: function () { console.log(someMessage); } }; }; setInterval(replaceThing, 1000);
代碼片斷作了一件事情:每次調用 replaceThing ,theThing 獲得一個包含一個大數組和一個新閉包(someMethod)的新對象。同時,變量 unused 是一個引用 originalThing 的閉包(先前的 replaceThing 又調用了 theThing )。思緒混亂了嗎?最重要的事情是,閉包的做用域一旦建立,它們有一樣的父級做用域,做用域是共享的。someMethod 能夠經過 theThing 使用,someMethod 與 unused 分享閉包做用域,儘管 unused 從未使用,它引用的 originalThing 迫使它保留在內存中(防止被回收)。當這段代碼反覆運行,就會看到內存佔用不斷上升(新建的多個originalThing一直被保存在內存中),垃圾回收器(GC)並沒有法下降內存佔用。本質上,閉包的鏈表已經建立,每個閉包做用域攜帶一個指向大數組的間接的引用,形成嚴重的內存泄漏。
這時候應該在 replaceThing 的最後添加 originalThing = null,主動解除對象引用。
timeline 標籤擅長作這些。在 Chrome 中打開例子,打開 Dev Tools ,切換到 timeline,勾選 memory 並點擊記錄按鈕,而後點擊頁面上的 The Button 按鈕。過一陣中止記錄看結果:
兩種跡象顯示出現了內存泄漏,圖中的 Nodes(綠線)和 JS heap(藍線)。Nodes 穩定增加,並未降低,這是個顯著的信號。
JS heap 的內存佔用也是穩定增加。因爲垃圾收集器的影響,並不那麼容易發現。圖中顯示內存佔用忽漲忽跌,實際上每一次下跌以後,JS heap 的大小都比原先大了。換言之,儘管垃圾收集器不斷的收集內存,內存仍是週期性的泄漏了。
https://github.com/yygmind/blog
http://www.cnblogs.com/vajoy/p/3703859.html
https://blog.csdn.net/yolo0927/article/details/80471220