You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
3:脱离 DOM 的引用
同样的 DOM 元素存在两个引用:一个在 DOM 树中,另一个在字典中。将来你决定删除这些行时,需要把两个引用都清除。
var elements = {
button: document.getElementById('button'),
image: document.getElementById('image'),
text: document.getElementById('text')
};
function doStuff() {
image.src = 'http://some.url/image';
button.click();
console.log(text.innerHTML);
// 更多逻辑
}
function removeButton() {
// 按钮是 body 的后代元素
document.body.removeChild(document.getElementById('button'));
// 此时,仍旧存在一个全局的 #button 的引用
// elements 字典。button 元素仍旧在内存中,不能被 GC 回收。
}
4:闭包
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);
内存泄露
程序的运行需要内存,只要有程序在运行,操作系统或者运行是就必须供给内存给程序。
JavaScript中的内存分为栈内存和堆内存。
JavaScript的变量分为原始数据类型和引用数据类型,原始数据类型有:Undefined,Null,Boolean,Number、String,引用数据类型有:Object,Function,Array。
其中原始数据存储在栈内存中,引用数据存储在堆内存中;
对于原始数据类型的值而言,其地址和具体内容都存在与栈内存中;而引用数据类型其地址存在于栈内存,其具体内容存在堆内存中。
栈内存运行效率比堆内存高,空间相对推内存来说较小,大小固定,堆内存反之
内存的生命周期:
1.当对象将被需要的时候为其分配内存
2.使用已分配的内存(读、写操作)
3.当对象不在被需要的时候,释放存储这个对象的内存
对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。
不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。
垃圾回收机制
JavaScript提供了自动内存管理,减轻程序员的负担,这被称为"垃圾回收机制"(garbage collector):找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是时时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。
引用计数
语言引擎有一张"引用表",保存了内存里面所有的资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。
如果一个值不再需要了,引用数却不为0,垃圾回收机制无法释放这块内存,从而导致内存泄漏。
数组[1, 2, 3, 4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为1。尽管后面的代码没有用到arr,它还是会持续占用内存。我们可以将arr重置为null,释放内存:
限制:循环引用
引用计数无法处理循环引用。比如两个对象被创建并互相引用,形成了一个循环。被调用后不会离开函数作用域,所以它们已经没有用了,可以被回收,但是引用计数算法考虑到他们至少被引用了一次,所以他们不会被回收:
标记-清除算法(Mark-and-sweep)
这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。
A、C、D、H、I为不能获得的对象,被垃圾回收机制回收
循环引用不再是问题
在上面的示例中,函数调用返回之后,两个对象从全局对象出发无法获取。因此,他们将会被垃圾回收器回收。
限制: 那些无法从根对象查询到的对象都将被清除
常见 JavaScript 内存泄漏
1、意外的全局变量
2:被遗忘的计时器或回调函数
与节点或数据关联的计时器不再需要,node 对象可以删除,可是,计时器回调函数仍然没被回收,bigData 中存储了大量数据,也不会被回收
3:脱离 DOM 的引用
同样的 DOM 元素存在两个引用:一个在 DOM 树中,另一个在字典中。将来你决定删除这些行时,需要把两个引用都清除。
4:闭包
闭包的作用域一旦创建,它们有同样的父级作用域,作用域是共享的。someMethod 可以通过 theThing 使用,someMethod 与 unused 分享闭包作用域,尽管 unused 从未使用,它引用的 originalThing 迫使它保留在内存中(防止被回收)。当这段代码反复运行,就会看到内存占用不断上升,垃圾回收器(GC)并无法降低内存占用。本质上,闭包的链表已经创建,每一个闭包作用域携带一个指向大数组的间接的引用,造成严重的内存泄漏。
学习文章:
JavaScript 内存泄漏教程
内存管理
4类 JavaScript 内存泄漏及如何避免
The text was updated successfully, but these errors were encountered: