javascript内存泄漏

简介: 当一个系统不能正确的管理他的内存分配时,这个系统就存在内存泄漏,这对系统来说是一个bug。内存泄漏的现象可以有程序调用失败、执行减慢等。 微软的Internet Explorer就存在一系列的内存泄漏,其中最严重的就算是执行Jscript时产生的泄漏。

当一个系统不能正确的管理他的内存分配时,这个系统就存在内存泄漏,这对系统来说是一个bug内存泄漏的现象可以有程序调用失败、执行减慢等。

微软的Internet Explorer就存在一系列的内存泄漏,其中最严重的就算是执行Jscript时产生的泄漏。当一个DOM对象包涵有一个JavaScript对象(例如一个事件处理函数)的引用,同时如果这个JavaScript对象又包涵该DOM对象,那么这个循环引用就形成了。这种结构本质上没有问题。此时,因为该DOM对象和这个事件处理函数并没有别的引用存在,那么垃圾回收器(一种自动的内存资源管理器)本应该把它们都回收点,并内存释放。JavaScript的垃圾回收器能够检测到这种循环引用,并不会对他产生困惑。但是不幸的是,IE DOM的内存并不能被Jscript所管理。他有他自己的内存管理系统,然而这套系统并不知道循环引用,使得一切都变得混乱。这就导致了,当循环引用形成的时候,内存释放工作不能完成,也就是产生了内存泄漏。长时间的内存泄漏就将产生内存的匮乏,使得浏览器因缺乏必要内存而崩溃?

我们可以来演示一下。在第一段程序-question1中,我们将动态创建以10个为一组共计10000个的DOM元素(<span>),创建10个然后删除在创建10个,如此循环。你在运行这段程序时打开Windows任务管理器,就可以观察到页面运行时PF(虚拟内存)使用率一直保持不变。PF使用率的变化可以视为内存分配是否无效的指标。

 

Question1

<html>
    <head>
        <title>Queue Test 1</title>
    </head>
    <body>
        <script>
            /*global setTimeout */
            (function (limit, delay) {
                var queue = new Array(10);
                var n = 0;
 
                function makeSpan(n) {
                    var s = document.createElement('span');
                    document.body.appendChild(s);
                    var t = document.createTextNode(' ' + n);
                    s.appendChild(t);
                    return s;
                }
 
                function process(n) {
                    queue.push(makeSpan(n));
                    var s = queue.shift();
                    if (s) {
                        s.parentNode.removeChild(s);
                    }
                }
 
                function loop() {
                    if (n < limit) {
                        process(n);
                        n += 1;
                        setTimeout(loop, delay);
                    }
                }
 
                loop();
            })(10000, 10);
        </script>
    </body>
html

 

接下来我们运行第二段程序queuetest2。除了做与queuetest1相同的事情以外,它还未每个元素添加了一个点击事件响应函数。在MozilaOpera上,虚拟PF利用率和queuetest1是一样的,但是在IE上我们可以看见由于内存泄漏而产生的每秒一兆的虚拟内存的稳定增量,通常这种泄露都不会被注意到。但是由于Ajax的日益流行,使得页面在浏览器的停留时间增长,使得问题变得常见了。

Question2

<html>

    <head><title>Queue Test 2</title>

    </head>

    <body>

        <script>

            /*global setTimeout */

            (function (limit, delay) {

                var queue = new Array(10);

                var n = 0;

 

                function makeSpan(n) {

                    var s = document.createElement('span');

                    document.body.appendChild(s);

                    var t = document.createTextNode(' ' + n);

                    s.appendChild(t);

                    s.onclick = function (e) {

                        s.style.backgroundColor = 'red';

                        alert(n);

                    };

                    return s;

                }

 

                function process(n) {

                    queue.push(makeSpan(n));

                    var s = queue.shift();

                    if (s) {

                        s.parentNode.removeChild(s);

                    }

                }

 

                function loop() {

                    if (n < limit) {

                        process(n);

                        n += 1;

                        setTimeout(loop, delay);

                    }

                }

 

                loop();

            })(10000, 10);

        </script>

    </body>

</html>

 

因为IE不能对循环引用进行回收,所以这个任务就落在了我们的肩上。如果我们明确的打破这个循环引用,那么IE就能够完成垃圾回收工作了。具微软的解释,引起内存泄漏的原因是闭包,然而这个结论肯定是非常错误的,并且这使得微软给开发者的建议也成了错误的建议。那么通过DOM来打破循环引用更简单,因为实际上不可能通过Jscript来实现。

当我们处理完一个元素后,我们必须通过把它所有的事件处理函数制空来达到破坏循环引用的目的。我们所需要做的就是把每个事件的处理函数设为空就可以了。我们甚至可以清理函数来完成这一工作。

清理函数将保存一份DOM元素的引用。它将循环检测这个元素的所有属性。如果发现了时间处理函数,就把它值为空。这样就破坏了循环引用,使得内存可以被回收释放。它同样也会检测该元素的子元素,打破他们的循环引用。这个清理函数,只在IE中有效果,对于MozillaOpera都无效。不管是用removeChild()或者是设置innerHTML属性的值,都应该在删除元素之前调用清理函数。

那么我们现在来运新第3个程序,queuetest3,在程序3里,元素在被删除之前都调用了清理函数。

Question3

<html>

    <head><title>Queue Test 3</title>

    </head>

    <body>

        <p>

            Queue Test 3 adds an event handler to each span, and removes it when

finished. See <a href="http://www.crockford.com/javascript/memory/leak.html">http://www.crockford.com/javascript/memory/leak.html</a>

        </p>

        <script>

            /*global onunload, setTimeout */

            (function (limit, delay) {

                var queue = new Array(10);

                var n = 0;

 

                function makeSpan(n) {

                    var s = document.createElement('span');

                    document.body.appendChild(s);

                    var t = document.createTextNode(' ' + n);

                    s.appendChild(t);

                    s.onclick = function (e) {

                        s.style.backgroundColor = 'red';

                        alert(n);

                    };

                    return s;

                }

 

                function purge(d) {

                    var a = d.attributes, i, l, n;

                    if (a) {

                        l = a.length;

                        for (i = 0; i < l; i += 1) {

                            n = a[i].name;

                            if (typeof d[n] === 'function') {

                                d[n] = null;

                            }

                        }

                    }

                    a = d.childNodes;

                    if (a) {

                        l = a.length;

                        for (i = 0; i < l; i += 1) {

                            purge(d.childNodes[i]);

                        }

                    }

                }

 

                function process(n) {

                    queue.push(makeSpan(n));

                    var s = queue.shift();

                    if (s) {

                        purge(s);

                        s.parentNode.removeChild(s);

                    }

                }

 

                function loop() {

                    if (n < limit) {

                        process(n);

                        n += 1;

                        setTimeout(loop, delay);

                    }

                }

                onunload = function (e) {

                    purge(document.body);

                };

 

                loop();

            })(10000, 10);

        </script>

    </body>

</html>

 

更新:微软发布了该问题的补丁:929874。如果你有十足的信心确保你所有的用户都可以获得该更新,那么你将不再需要上面的清理函数。但不幸的是,这不可能如你所愿,所以可能清理工作在IE6被淘汰之前还是有必要的。

这就是web的天性,有清理不完的bugs.

相关文章
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
JavaScript 前端开发 Java
避免 JavaScript 中的内存泄漏
【10月更文挑战第30天】避免JavaScript中的内存泄漏问题需要开发者对变量引用、事件监听器管理、DOM元素操作以及异步操作等方面有深入的理解和注意。通过遵循良好的编程实践和及时清理不再使用的资源,可以有效地减少内存泄漏的风险,提高JavaScript应用程序的性能和稳定性。
|
存储 JavaScript 前端开发
JS 中的内存管理
【10月更文挑战第17天】了解和掌握 JavaScript 中的内存管理是非常重要的。通过合理的内存分配、及时的垃圾回收以及避免内存泄漏等措施,可以确保代码的高效运行和稳定性。同时,不断关注内存管理的最新发展动态,以便更好地应对各种挑战。在实际开发中要时刻关注内存使用情况,以提升应用的性能和质量。
205 1
|
存储 缓存 监控
如何使用内存监控工具来优化 Node.js 应用的性能
需要注意的是,不同的内存监控工具可能具有不同的功能和特点,在使用时需要根据具体工具的要求和操作指南进行正确使用和分析。
815 158
|
监控 JavaScript 算法
如何使用内存监控工具来定位和解决Node.js应用中的性能问题?
总之,利用内存监控工具结合代码分析和业务理解,能够逐步定位和解决 Node.js 应用中的性能问题,提高应用的运行效率和稳定性。需要耐心和细致地进行排查和优化,不断提升应用的性能表现。
760 174
|
监控 JavaScript 数据库连接
解读Node.js内存监控工具生成的报告
需要注意的是,不同的内存监控工具可能会有不同的报告格式和内容,具体的解读方法可能会有所差异。因此,在使用具体工具时,还需要参考其相关的文档和说明,以更好地理解和利用报告中的信息。通过深入解读内存监控报告,我们可以不断优化 Node.js 应用的内存使用,提高其性能和稳定性。
672 175
|
监控 JavaScript
选择适合自己的Node.js内存监控工具
选择合适的内存监控工具是优化 Node.js 应用内存使用的重要一步,它可以帮助你更好地了解内存状况,及时发现问题并采取措施,提高应用的性能和稳定性。
823 174
|
缓存 监控 JavaScript
避免在Node.js中出现内存泄漏
总之,避免内存泄漏需要在开发过程中保持谨慎和细心,遵循最佳实践,不断优化和改进代码。同时,定期进行内存管理的检查和维护也是非常重要的。通过采取这些措施,可以有效地降低 Node.js 应用中出现内存泄漏的风险,确保应用的稳定和性能。
475 159
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
1122 159
|
存储 缓存 JavaScript
如何优化Node.js应用的内存使用以提高性能?
通过以上多种方法的综合运用,可以有效地优化 Node.js 应用的内存使用,提高性能,提升用户体验。同时,不断关注内存管理的最新技术和最佳实践,持续改进应用的性能表现。
867 62