Memory Leak 是最難排查調試的 Bug 種類之一,由於內存泄漏是個 undecidable problem,只有開發者才能明確一塊內存是否是須要被回收。再加上內存泄漏也沒有特定的報錯信息,只能經過必定時間段的日誌來判斷是否存在內存泄漏。你們熟悉的經常使用調試工具對排查內存泄漏也沒有用武之地。固然了,除了專門用於排查內存泄漏的工具(抓取Heap之類的工具)以外。javascript
對於不一樣的語言,各類排查內存泄漏的方式方法也不盡相同。對於 JavaScript 來講,針對不一樣的平臺,調試工具也是不同的,最經常使用的恐怕仍是 Chrome 自帶的各類利器(針對 browser 也好,nodeJS 也好)都有不錯的使用體驗,網上也有不少使用教程。前端
此次我想給你們介紹的內存泄漏的定位方法,並不是工具的使用。而是一些經驗的總結,也就是我所知道的 VueJS SSR 中最容易出現內存泄漏的地方,若是你們知道更多 VueJS SSR 內存泄漏點,能夠在評論處留言告訴更多的人。vue
遇到過 VueJS SSR 內存泄漏的朋友可能知道,針對 VueJS SSR 內存泄漏的排查,與普通 NodeJS 和 Browser 平臺相比是要麻煩不少的。若是你使用了 webpack-dev-server 在本地調試,你會發現經常使用的內存泄漏工具毫無用武之地,由於抓取到的信息不只包括 VueJS SSR 進程信息,還包含了 Webpack 的進程信息,甚至還有 webpack-dev-server 的各類堆信息。固然了,你也能夠經過各類手段來過濾掉無關的信息,從而只剩下 VueJS SSR 的堆信息。java
我在排查咱們組項目內存泄漏的時候,動用了各類常規工具,但最終發現 VueJS SSR 的內存泄漏有很大可能性出如今如下地方,也就說若是,你碰巧也有 VueJS SSR 內存泄漏的問題,先不要使用內存泄漏排查工具,首先從下面幾個地方着手,看看是否有內存泄漏的邏輯。可能直擊要害,節約時間。node
如下是 VueJS 開發者看過無數次的說明圖,我還請你們再多看一遍webpack
在官方文檔裏,有這麼一句話:git
Since there are no dynamic updates, of all the lifecycle hooks, only beforeCreate and created will be called during SSR. This means any code inside other lifecycle hooks such as beforeMount or mounted will only be executed on the client.github
也就是說 SSR 跟前端繪製同樣,也有生命週期,只不過 SSR 的生命週期裏只有 beforeCreate 和 created 。web
因此你須要首先排查你的組件的 beforeCreate 和 created 裏面是否有內存泄漏的代碼,或者他們是否調用了會內存泄漏的代碼。後端
路由也是會引發 SSR 內存泄漏的地方之一
跟生命週期不一樣,全部的 route guard 都會在 SSR 運行。他們分別都是
還須要特別注意的地方就是 Date-prefetch 的地方,裏面很容易出現內存泄漏的代碼。 所謂 Date-prefetch 就是自定義實現的,在SSR處提早獲取第三方數據,用於繪製的過程。
這個內存泄漏的點想必你們都已經熟知,做者也在github上詳細闡述過:GitHub issue
簡單來講,就是 global mixin 會給每一個 Vue 實例一個拷貝,而不是引用。
以上列舉了一些可能出現內存泄漏的地方,那麼具體怎麼樣的代碼纔會引發內存泄漏呢?引發代碼泄漏的例子網上有不少,我在這裏想給你們介紹幾種常見的泄漏例子。
function foo(arg) {
bar = "this is a hidden global variable";
}
複製代碼
以上的代碼會順利運行,可是由於不當心聲明瞭一個 bar
的變量。至關於:
function foo(arg) {
window.bar = "this is an explicit global variable";
}
複製代碼
生成了一個全局變量 window.bar
若是不手動回收,這個全局變量會一直存在於內存中,不會被CG回收。聚沙成塔,最後形成內存泄漏。
如今你們都是在各類模塊化(CommonJS/AMD/CMD/etc..)以後的環境下進行開發,這種全局變量的內存泄漏的問題基本上是被消除了。可是要提醒你們,因爲JavaScript的各類特性,會有不少意想不到的情況發生。當摸不清頭腦的時候,能夠嘗試從這些特性出發找到問題。
請你們先看如下的例子
var someResource = getData();
setInterval(function() {
var node = document.getElementById('Node');
if(node) {
// Do stuff with node and someResource.
node.innerHTML = JSON.stringify(someResource));
}
}, 1000);
複製代碼
乍一看沒啥問題,以後若是 Node
節點從DOM上被移除,由於上面的 callback 對 Node
節點有引用,因此 Node
節點會一直常駐內存,不會被CG回收。
要避免以上問題,就要養成 removeEventListener
和 clearInterval
的習慣。
var someResource = getData();
var interval = setInterval(function() {
var node = document.getElementById('Node');
if(node) {
// Do stuff with node and someResource.
node.innerHTML = JSON.stringify(someResource));
} else {
// Remove Timer
clearInterval(interval);
}
}, 1000);
複製代碼
還好比:
var element = document.getElementById('button');
function onClick(event) {
element.innerHtml = 'text';
}
element.addEventListener('click', onClick);
// Do stuff
element.removeEventListener('click', onClick);
element.parentNode.removeChild(element);
// Now when element goes out of scope,
// both element and onClick will be collected even in old browsers that don't
// handle cycles well.
複製代碼
在 addEventListener
以後已經要記得 removeEventListener
閉包形成內存泄漏的狀況比較複雜,並且較難查找。限於本文主旨,不作原理說明。
可是,在這裏我給你們推薦一篇很是不錯的文章,詳細地介紹了閉包是如何形成內存泄漏的過程:An interesting kind of JavaScript memory leak
我的認爲 VueJS SSR 後端繪製內存泄漏形成影響要比普通的 VueJS 前端內存泄漏形成的影響要更大。
前端內存泄漏的影響,都是發生在客戶機器上,並且基本上現代瀏覽器也會作好保護機制,通常自行刷新以後都會解決。可是,一旦後端繪製內存泄漏形成宕機以後,整個服務器都會受影響,危險性更大,搞很差年終獎就沒了。
前端工程師通常都是關注於瀏覽器端表現,在開發過程當中的內存泄漏問題不太在乎也不太容易被發現。通常都是在項目上線一段時間以後,才發現內存泄漏的狀況。那個時候再去着手,可能會有些無從下手或者手忙腳亂。
那麼,就讓咱們在開發的時候開始關注內存泄漏問題,將 VueJS SSR 後端繪製內存泄漏問題扼殺於襁褓之中。