小编典典

使用 Chrome 查找 JavaScript 内存泄漏

all

我创建了一个非常简单的测试用例,它创建一个 Backbone
视图,将一个处理程序附加到一个事件,并实例化一个用户定义的类。我相信通过单击此示例中的“删除”按钮,所有内容都会被清理干净,并且应该没有内存泄漏。

代码的 jsfiddle 在这里:http: //jsfiddle.net/4QhR2/

// scope everything to a function
function main() {

    function MyWrapper() {
        this.element = null;
    }
    MyWrapper.prototype.set = function(elem) {
        this.element = elem;
    }
    MyWrapper.prototype.get = function() {
        return this.element;
    }

    var MyView = Backbone.View.extend({
        tagName : "div",
        id : "view",
        events : {
            "click #button" : "onButton",
        },    
        initialize : function(options) {        
            // done for demo purposes only, should be using templates
            this.html_text = "<input type='text' id='textbox' /><button id='button'>Remove</button>";        
            this.listenTo(this,"all",function(){console.log("Event: "+arguments[0]);});
        },
        render : function() {        
            this.$el.html(this.html_text);

            this.wrapper = new MyWrapper();
            this.wrapper.set(this.$("#textbox"));
            this.wrapper.get().val("placeholder");

            return this;
        },
        onButton : function() {
            // assume this gets .remove() called on subviews (if they existed)
            this.trigger("cleanup");
            this.remove();
        }
    });

    var view = new MyView();
    $("#content").append(view.render().el);
}

main();

但是,我不清楚如何使用谷歌浏览器的分析器来验证事实是否如此。堆分析器快照上显示了无数的东西,我不知道如何解码好/坏。到目前为止,我看到的教程要么只是告诉我“使用快照分析器”,要么给我一个关于整个分析器如何工作的非常详细的宣言。是否可以仅将分析器用作工具,还是我真的必须了解整个事物是如何设计的?

编辑: 像这样的教程:

Gmail 内存泄漏修复

使用开发工具

据我所见,它们代表了一些更强的材料。然而,除了介绍 3 Snapshot Technique
的概念之外,我发现它们在实践知识方面提供的很少(对于像我这样的初学者)。’使用 DevTools’
教程没有通过一个真实的例子来工作,所以它对事物的模糊和一般的概念描述并没有太大的帮助。至于“Gmail”示例:

所以你发现了一个漏洞。 怎么办?

  • 在 Profiles 面板的下半部分检查泄漏对象的保留路径

  • 如果不能轻易推断分配站点(即事件侦听器):

  • 通过 JS 控制台检测保留对象的构造函数以保存分配的堆栈跟踪

  • 使用闭包?启用适当的现有标志(即 goog.events.Listener.ENABLE_MONITORING)以在构造期间设置
    creationStack 属性

读完之后,我发现自己更加困惑,而不是更少。再说一次,它只是告诉我 做事 ,而不是告诉我 如何
去做。从我的角度来看,那里的所有信息要么太模糊,要么只对已经了解该过程的人有意义。


阅读 70

收藏
2022-08-29

共1个答案

小编典典

查找内存泄漏的一个很好的工作流程是 三个快照 技术,Loreena Lee 和 Gmail 团队首先使用它来解决他们的一些内存问题。步骤一般是:

  • 拍摄堆快照。
  • 做东西。
  • 拍摄另一个堆快照。
  • 重复同样的事情。
  • 拍摄另一个堆快照。
  • 在快照 3 的“摘要”视图中筛选在快照 1 和 2 之间分配的对象。

对于您的示例,我已调整代码以显示此过程(您可以在此处找到它)将主干视图的创建延迟到“开始”按钮的单击事件。现在:

  • 运行 HTML(使用此地址保存在本地)并拍摄快照。
  • 单击开始创建视图。
  • 再拍一张快照。
  • 单击删除。
  • 再拍一张快照。
  • 在快照 3 的“摘要”视图中筛选在快照 1 和 2 之间分配的对象。

现在您已准备好查找内存泄漏!

您会注意到几种不同颜色的节点。红色节点没有来自 Javascript 的直接引用,但它们是活动的,因为它们是分离的 DOM 树的一部分。树中可能有一个从
Javascript 引用的节点(可能作为闭包或变量),但巧合的是,它阻止了整个 DOM 树被垃圾收集。

在此处输入图像描述

然而,黄色节点确实有来自 Javascript 的直接引用。在同一个分离的 DOM 树中查找黄色节点以从您的 Javascript 中定位引用。应该有一个从
DOM 窗口到元素的属性链。

在您的特定页面中,您可以看到标记为红色的 HTML Div 元素。如果您展开该元素,您将看到它被“缓存”函数引用。

在此处输入图像描述

选择该行并在您的控制台中输入 $0,您将看到实际的功能和位置:

>$0
function cache( key, value ) {
        // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
        if ( keys.push( key += " " ) > Expr.cacheLength ) {
            // Only keep the most recent entries
            delete cache[ keys.shift() ];
        }
        return (cache[ key ] = value);
    }                                                     jquery-2.0.2.js:1166

这是您的元素被引用的地方。不幸的是,您无能为力,它是 jQuery 的内部机制。但是,仅出于测试目的,请执行该功能并将方法更改为:

function cache( key, value ) {
    return value;
}

现在,如果您重复该过程,您将看不到任何红色节点 :)

文档:

2022-08-29