内存泄漏问题,及解决办法

176 阅读3分钟

内存泄漏是什么

内存泄漏也称作‘存储渗漏’,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。

那些操作会引发内存泄漏

  1. 垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0 (没有其他对象引用过该对象),或对该对象的唯一引用是循环的,那么该对象的内存即可回收。
  2. setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
  3. 闭包、控制台日志、循环(在两个对象彼此引发且彼此保留时,就会产生一个循环)

内存泄漏的解决办法

  1、global variables:对未声明的变量的引用在全局对象内创建一个新变量。在浏览器中,全局对象就是 window。

function foo(arg) {  
       bar = 'some text';     // 等同于 window.bar = 'some text';  
}  

1.1)解决:

  1.1.1)创建意外的全局变量

function foo() {  
     this.var1 = 'potential accident'  
} 

  1.1.2)可以在 JavaScript 文件开头添加 “use strict”,使用严格模式。这样在严格模式 下解析 JavaScript 可以防止意外的全局变量

  1.1.3)在使用完之后,对其赋值为 null 或者重新分配
1.2)被忘记的 Timers 或者 callbacks

  在 JavaScript 中使用 setInterval 非常常见

  大多数库都会提供观察者或者其它工具来处理回调函数,在他们自己的实例变为不可达时, 会让回调函数也变为不可达的。对于 setInterval,下面这样的代码是非常常见的:

var serverData = loadData();  
setInterval(function() {  
var renderer = document.getElementById('renderer');   
if(renderer) {   
renderer.innerHTML = JSON.stringify(serverData);  
} 
}, 5000);    //This will be executed every ~5 seconds. 

 这个例子阐述着 timers 可能发生的情况:计时器会引用不再需要的节点或数据

 1.3)闭包:一个可以访问外部(封闭)函数变量的内部函数

 JavaScript 开发的一个关键方面就是闭包:一个可以访问外部(封闭)函数变量的内部函数。 由于 JavaScript 运行时的实现细节,可以通过以下方式泄漏内存:

var theThing = null;  
var replaceThing = function () {  
  var originalThing = theThing;   
var unused = function () {  
    if (originalThing) // a reference to 'originalThing'  
     console.log("hi");  
    };  
    theThing = {   
      longStr: new Array(1000000).join('*'),  
      someMethod: function () {   
        console.log("message");  
     }   
  };   
};  
setInterval(replaceThing, 1000);   

 1.4)DOM 引用
有时候,在数据结构中存储 DOM 结构是有用的。假设要快速更新表中的几行内容。将每行 DOM 的引用 存储在字典或数组中可能是有意义的。当这种情况发生时,就会保留同一 DOM 元素的两份引用:一个在 DOM 树种,另一个在字典中。如果将来某个时候你决定要删除这些行,则需要让两个引用都不可达。

var elements = {  
    button: document.getElementById('button'),   
    image: document.getElementById('image')   
};  
function doStuff() {   
    elements.image.src = 'http://example.com/xxxxxxxx.png';   
}   
function removeImage() { // The image is a direct child of the body element.     
    document.body.removeChild(document.getElementById('image'));   
    // At this point, we still have a reference to #button in the  
    //global elements object. In other words, the button element is   
    //still in memory and cannot be collected by the GC.
}