清理内存泄漏

时间:2015-03-25 03:23:58

标签: javascript jquery memory-leaks

我使用jQuery来克隆元素,然后保存对该克隆中元素的引用。很久以后删除克隆。这是一个基本的例子:

HTML

<div> <span></span> </div>

脚本

var i, $clone, $span,
    $saved = $('span'),
    $orig = $('div');

for (i = 0; i < 100; i++) {
    $clone = $orig.clone().appendTo('body');
    $span = $clone.find('span');

    $saved = $saved.add($span);
    $clone.remove();
}
console.log( 'leaking = ', $saved.length);

控制台日志输出的长度为101

我需要清理$saved jQuery对象并删除对不再附加到DOM的元素的引用。所以我写了这个基本功能来清理它。

var cleanUpLeaks = function ($el) {
    var el, remove,
    index = $el.length - 1;
    while (index >= 0) {
        el = $el[index];
        remove = true;
        while (el) {
            el = el.parentNode;
            if (el && el.nodeName === 'HTML') {
                remove = false;
                break;
            }
        }
        if (remove) {
            $el.splice(index, 1);
        }
        index--;
    }
    return $el;
};

console.log( 'cleaned up = ', cleanUpLeaks( $saved ).length );

这次控制台输出1

所以现在我的问题是:

  • 我怎么能首先防止内存泄漏?
  • 如果这不可能,我应该在.splice()函数中使用cleanUpLeaks来删除引用吗?或者按照建议将引用设置为null会更好吗?因为当我将其设置为null时,$saved的长度保持为101

演示:http://jsfiddle.net/Mottie/6q2hjazg/


详细说明,我在$saved中保存了对范围的引用。还有其他功能使用此值进行样式设置等。这是一个非常基本的例子;并且,我没有立即删除克隆后将其附加到正文,这是在这里完成以显示内存泄漏是如何发生的。

2 个答案:

答案 0 :(得分:1)

一种可能的解决方案是,您可以从AngularJS的书籍和猴子补丁jQuery中删除一个元素,以便在删除元素时触发事件。然后,您可以为该事件添加处理程序,并将$saved的状态恢复为添加$span之前的状态。

首先,猴子补丁jQuery(取自AngularJS source):

// All nodes removed from the DOM via various jQuery APIs like .remove()
// are passed through jQuery.cleanData. Monkey-patch this method to fire
// the $destroy event on all removed nodes.
var originalCleanData = jQuery.cleanData;
var skipDestroyOnNextJQueryCleanData;
jQuery.cleanData = function (elems) {
    var events;
    if (!skipDestroyOnNextJQueryCleanData) {
        for (var i = 0, elem;
        (elem = elems[i]) != null; i++) {
            events = jQuery._data(elem, "events");
            if (events && events.$destroy) {
                jQuery(elem).triggerHandler('$destroy');
            }
        }
    } else {
        skipDestroyOnNextJQueryCleanData = false;
    }
    originalCleanData(elems);
};

接下来,添加$destroy事件处理程序并恢复捕获的原始状态$saved

var i, $clone, $span,
    $saved = $('span'),
    $orig = $('div');

for (i = 0; i < 100; i++) {
    (function ($originalSaved) {
        $clone = $orig.clone().appendTo('body');
        $span = $clone.find('span');

        $clone.on('$destroy', function () {
            $saved = $originalSaved;
            $originalSaved = null;
        });
        $saved = $saved.add($span);

        $clone.remove();
    })($saved);
}
console.log('original length = ', $saved.length); // => 1

这是a jsFiddle这个有效。在我的Chrome测试中,这并没有引入额外的泄漏。

答案 1 :(得分:1)

这里更好的解决方案是停止在持久化jQuery变量中保存动态DOM元素。如果您的页面定期从DOM中删除内容,那么将它们保存在持久性jQuery对象中只会让您不得不处理内存泄漏,而不是将设计更改为不必保存对DOM元素的引用的设计。所有。

如果相反,您只是使用文档中其他地方未使用的特定类名来标记有趣的元素,您可以使用简单的jQuery选择器查询随时生成所需的元素列表,您将完全没有问题泄漏,因为您不会在持久变量中保留DOM引用。