使用Chrome查找JavaScript内存泄漏

Ele*_*One 158 javascript memory-leaks google-chrome backbone.js

我创建了一个非常简单的测试用例,它创建了一个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();
Run Code Online (Sandbox Code Playgroud)

但是,我不清楚如何使用谷歌浏览器的分析器来验证这是事实.堆分析器快照上有很多东西出现,我不知道如何解码好/坏的东西.到目前为止我在其上看到的教程或者只是告诉我"使用快照分析器"或者给我一个关于整个分析器如何工作的非常详细的宣言.是否可以将探查器用作工具,或者我是否真的必须了解整个工具是如何设计的?

编辑:像这样的教程:

Gmail内存泄漏修复

使用DevTools

从我所看到的,代表一些更强大的材料.然而,除了引入3快照技术的概念之外,我发现它们在实践知识方面提供的很少(对于像我这样的初学者)."使用DevTools"教程并不适用于一个真实的例子,因此它对事物的模糊和一般概念性描述并没有太大帮助.至于'Gmail'示例:

所以你发现了泄漏.怎么办?

  • 检查"配置文件"面板下半部分中泄漏对象的保留路径

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

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

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

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

其中一些更具体的问题已在@Jonathan Naguin的答案中提出.

Jon*_*uin 198

找到内存泄漏的良好工作流程是三种快照技术,Loreena Lee和Gmail团队首先使用它来解决一些内存问题.一般来说,步骤如下:

  • 拍摄快照.
  • 做东西.
  • 拍摄另一个堆快照.
  • 重复相同的东西.
  • 拍摄另一个堆快照.
  • 过滤快照3的"摘要"视图中快照1和2之间分配的对象.

对于您的示例,我已经调整了代码以显示此过程(您可以在此处找到)延迟创建Backbone视图,直到"开始"按钮的单击事件.现在:

  • 运行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
Run Code Online (Sandbox Code Playgroud)

这是您的元素被引用的位置.不幸的是,你没有太多可以做的,它是jQuery的内部机制.但是,仅用于测试目的,去功能并将方法更改为:

function cache( key, value ) {
    return value;
}
Run Code Online (Sandbox Code Playgroud)

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

文档:

  • 我感谢您的努力.实际上,教程中经常提到三种快照技术.不幸的是,细节往往被遗漏.例如,我很欣赏在控制台中引入`$ 0`功能,这对我来说是新的 - 当然,我不知道它在做什么或你怎么知道使用它(`$ 1`似乎没用,而$ 2`好像做同样的事情).其次,你怎么知道突出显示函数cache()`中的`#button行而不是其他任何几十行?最后,`NodeList`和`HTMLInputElement`中也有红色节点,但我无法弄明白. (7认同)
  • 你怎么知道`cache`行包含信息而其他人没有?有许多分支的距离比`cache`更低.我不知道你怎么知道`HTMLInputElement`是`HTMLDivElement`的孩子.我看到它在里面被引用("HTMLDivElement中的本机"),但它也引用了它自己和两个`HTMLButtonElement',这对我来说没有意义.我当然感谢你确定这个例子的答案,但我真的不知道如何将其概括为其他问题. (6认同)
  • 在快照3的"摘要"视图中,快照1和2之间分配的过滤器对象是什么意思? (4认同)
  • 这很奇怪,我正在使用你的例子,我得到了一个不同于你的结果(从你的截图).不过,我非常感谢你的帮助.我觉得我现在已经足够了,当我有一个真实的例子需要特别的帮助时,我会在这里创建一个新问题.再次感谢. (2认同)
  • 可在此处找到$ 0的说明:https://developer.chrome.com/devtools/docs/commandline-api#0-4 (2认同)

ric*_*ggs 7

这里有一个关于jsfiddle的内存分析的提示:使用以下URL来隔离你的jsfiddle结果,它删除所有的jsfiddle框架并只加载你的结果.

http://jsfiddle.net/4QhR2/show/

在阅读以下文档之前,我始终无法弄清楚如何使用Timeline和Profiler来跟踪内存泄漏.阅读标题为"对象分配跟踪器"的部分后,我能够使用"记录堆分配"工具,并跟踪一些分离的DOM节点.

我通过从jQuery事件绑定切换到使用Backbone事件委托来解决问题.我的理解是,如果你打电话,新版本的Backbone会自动为你解开事件View.remove().自己执行一些演示,它们设置有内存泄漏,供您识别.如果您在学习本文档后仍未获得问题,请随时在此处提问.

https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling


Kon*_*nev 6

基本上,您需要查看堆快照中的对象数.如果两个快照之间的对象数量增加,并且您已经丢弃了对象,则会出现内存泄漏.我的建议是在代码中查找不会分离的事件处理程序.

  • 例如,如果我查看jsfiddle的堆快照,在单击"删除"之前,存在的对象远远超过100,000个.我在哪里寻找我的jsfiddle代码实际创建的对象?我认为`Window/http:// jsfiddle.net/4QhR2/show`可能很有用,但它只是无穷无尽的功能.我不知道那里发生了什么. (3认同)
  • 确实.我强调当存在数千个物体时,知道要寻找什么是多么困难. (2认同)

令狐葱*_*令狐葱 5

谷歌有一个介绍视频,对查找JavaScript内存泄漏非常有帮助。

https://www.youtube.com/watch?v=L3ugr9BJqIs