寫在最前
本次的分享一段代碼來重新認(rèn)識在V8的垃圾回收機制败明。
歡迎關(guān)注我的博客隘马,不定期更新中——
一段來自meteor工程師提出的導(dǎo)致內(nèi)存泄漏的代碼
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)
打印內(nèi)存快照
1、首先打開一個空白的html肩刃,以防其他變量干擾祟霍。
2、將這段代碼輸入到命令行中盈包,并運行一次replaceThing()
3沸呐、打印堆快照:
4、在window/:file// window/下找到在控制臺打印的變量
可以看出此時的someMethod雖然沒有在函數(shù)體中留有對originalThing的引用呢燥,但事實是函數(shù)運行后someMethod被輸出到了全局崭添,其內(nèi)部有著對originalThing的引用導(dǎo)致其不會得到釋放∨寻保可想而知如果繼續(xù)運行replaceThing呼渣,那么originalThing會被賦值為theThing同時其中的someMethod還保有著上一次originalThing對象引用,從而形成了一個循環(huán)引用寞埠,內(nèi)部變量全都不會被釋放掉從而導(dǎo)致內(nèi)存的增長屁置。運行多次后打印快照可看到如下結(jié)果:
從打印快照的結(jié)果不難理解之前所說的由于循環(huán)引用而導(dǎo)致內(nèi)部變量釋放不掉從而內(nèi)存占用過多的事實。
So Why仁连?
我們可以知道在這個最關(guān)鍵的someMethod方法中并沒有對originalThing的引用蓝角!只有unused方法引用了originalThing,但unused方法又沒有形成閉包饭冬。那么到底是哪里疏漏了呢使鹅?
來看下來自meteor-blog的解釋:
Well, the typical way that closures are implemented is that every function object has a link to a dictionary-style object representing its lexical scope. If both functions defined inside replaceThing actually used originalThing, it would be important that they both get the same object, even if originalThing gets assigned to over and over, so both functions share the same lexical environment. Now, Chrome’s V8 JavaScript engine is apparently smart enough to keep variables out of the lexical environment if they aren’t used by any closures - from the Meteor blog.
原文出自Node.js Garbage Collection Explained
個人認(rèn)為關(guān)鍵在于這句話:
If both functions defined inside replaceThing actually used originalThing, it would be important that they both get the same object, even if originalThing gets assigned to over and over, so both functions share the same lexical environment.
那么個人理解為:someMethod與unused方法定義在了同一個父級作用域內(nèi)也就是同一個函數(shù)內(nèi),那么這兩個方法將共享相同的作用域昌抠,可見unused持有了對originalThing引用患朱,即便它沒有被調(diào)用,同時someMethod形成閉包導(dǎo)出到全局炊苫,這個時候someMethod會一直保持裁厅,同時由于共享了作用域則強制originalThing不被回收冰沙。故而導(dǎo)致最后的內(nèi)存泄漏。也就是我們在上圖中看到的someMethod方法在context中嵌套包含了originalThing對象姐直,導(dǎo)致內(nèi)存增長的結(jié)果倦淀。
最后
不定時更新中——
有問題歡迎在issues下交流。