backbone.js 使用Chrome查找JavaScript内存泄漏

ru9i0ody  于 2022-11-10  发布在  Java
关注(0)|答案(9)|浏览(333)

我已经创建了一个非常简单的测试用例,它创建了一个Backbone视图,将一个处理程序附加到一个事件,并示例化一个用户定义的类。我相信,通过单击本示例中的“Remove”按钮,所有内容都将被清除,并且不应该有内存泄漏。
下面是代码的jsfidle: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();

但是,我不清楚如何使用GoogleChrome的分析器来验证事实是否如此。堆分析器快照上显示了大量的内容,到目前为止,我所看到的教程要么只是告诉我“使用快照分析器”,要么就整个分析器的工作原理给予了我一个非常详细的宣言。有没有可能只把分析器作为一个工具来使用,或者我真的必须了解整个事情是如何设计的?

**编辑:**类似以下教程:

Gmail内存泄漏修复
Using DevTools
从我所看到的情况来看,它们代表了一些较强的材料。然而,除了介绍 *3快照技术 * 的概念外,我发现它们在实践知识方面提供的很少(对于像我这样的初学者)。“使用DevTools”教程并不通过一真实的例子来工作,因此它对事物模糊而笼统的概念性描述并没有太大的帮助。
"所以你发现了漏洞,现在怎么办“

  • 在“配置文件”面板的下半部分中检查泄漏对象的保留路径
  • 如果无法轻松推断分配站点(即事件侦听器):
  • 通过JS控制台检测保留对象的构造函数,以保存分配的堆栈跟踪
  • 使用Closure-启用相应的现有标志(即goog.events.Listener.ENABLE_MONITORING),以便在构造期间设置creationStack属性

阅读了这篇文章后,我发现自己更加困惑,而不是更少。再说一次,它只是告诉我“做”事情,而不是“如何”去做。在我看来,所有的信息要么太模糊,要么只有对已经了解过程的人才有意义。
其中一些更具体的问题已经在下面的@乔纳森·纳金的回答中提出。

pkwftd7m

pkwftd7m1#

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

  • 建立堆积快照集。
  • 做点什么。
  • 获取另一个堆快照。
  • 重复同样的事情。
  • 获取另一个堆快照。
  • 在快照3的“摘要”视图中筛选快照1和快照2之间分配的对象。

对于您的示例,我已经修改了代码来显示这个过程(您可以找到它here),它将 Backbone 视图的创建延迟到Start按钮的单击事件。现在:

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

现在您可以开始查找内存泄漏了!
你会注意到一些不同颜色的节点。红色节点没有直接从Javascript引用到它们,但它们是活的,因为它们是分离的DOM树的一部分。树中可能有一个节点从Javascript引用(可能作为闭包或变量),但碰巧阻止了整个DOM树被垃圾收集。

但是黄色节点确实有来自Javascript的直接引用。在同一个分离的DOM树中查找黄色节点以定位来自Javascript的引用。应该有一个从DOM窗口到元素的属性链。
在你的页面中,你可以看到一个标记为红色的HTML Div元素。如果你展开这个元素,你会看到它被一个“cache”函数引用了。

选择该行并在控制台中键入$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;
}

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

ax6ht2ek

ax6ht2ek2#

下面是一个关于jsfiddle内存分析的技巧:使用以下URL隔离jsfiddle结果,它将删除所有jsfiddle框架并仅加载您的结果。
http://jsfiddle.net/4QhR2/show/
我一直不知道如何使用Timeline和Profiler来跟踪内存泄漏,直到我阅读了下面的文档。在阅读了标题为“对象分配跟踪器”的部分后,我能够使用“记录堆分配”工具,并跟踪一些分离的DOM节点。
我通过从jQuery事件绑定切换到使用Backbone事件委托来修复这个问题。据我所知,如果你调用View.remove(),较新版本的Backbone会自动为你解除事件绑定。你自己执行一些演示,它们设置了内存泄漏以供你识别。如果你在学习了本文档后仍然不明白,请随时在这里提问。
https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling

k3fezbri

k3fezbri3#

基本上,你需要查看堆快照中的对象数量。如果两个快照之间的对象数量增加,并且你已经处理了对象,那么你就有内存泄漏。我的建议是在你的代码中寻找没有被分离的事件处理程序。

xcitsw88

xcitsw884#

有一个来自谷歌的介绍视频,这将对查找JavaScript内存泄漏非常有帮助。
https://www.youtube.com/watch?v=L3ugr9BJqIs

beq87vna

beq87vna5#

您还可能需要阅读:
http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/
它解释了chrome开发工具的使用,并给出了一些关于如何使用堆快照比较和可用的不同hep快照视图来确认和定位内存泄漏的分步建议。

qnyhuwrf

qnyhuwrf6#

你也可以查看开发者工具中的Timeline标签。记录你的应用的使用情况,并关注DOM节点和事件监听器计数。
如果内存图形确实指示内存泄漏,则可以使用分析器来找出泄漏的内容。

6kkfgxo0

6kkfgxo07#

关于使用Chrome Developer工具识别内存泄漏的几个重要注意事项:
1)Chrome本身对于某些元素有内存泄漏,比如密码和数字字段。https://bugs.chromium.org/p/chromium/issues/detail?id=967438。避免在调试时使用这些元素,因为在搜索分离的元素时,它们会污染堆快照。
2)避免在浏览器控制台中记录 * 任何内容 *。Chrome不会对写入控制台的对象进行垃圾收集,从而影响您的结果。您可以通过在脚本/页面的开头放置以下代码来隐藏输出:

console.log = function() {};
console.warn = console.log;
console.error = console.log;

3)使用堆快照并搜索“detach”来识别分离的DOM元素。通过悬停对象,您可以访问所有属性,包括 idouterHTML,这可能有助于识别每个元素。x1c 0d1x如果分离的元素仍然太通用而无法识别,请在运行测试之前使用浏览器控制台为它们分配唯一的ID,例如:

var divs = document.querySelectorAll("div");
for (var i = 0 ; i < divs.length ; i++)
{
    divs[i].id = divs[i].id || "AutoId_" + i;
}
divs = null; // Free memory

现在,当您用标识分离的元素时,比如说id=“AutoId_49”,重新加载页面,再次执行上面的代码片段,然后使用DOM检查器或document.querySelector(..)找到id=“AutoId_49”的元素。当然,这仅在页面内容可预测时有效。

如何运行测试以识别内存泄漏

1)加载页面(控制台输出已取消!)
2)在页面上执行可能导致内存泄漏操作
3)使用开发人员工具获取堆快照并搜索“detach”
4)将鼠标悬停在元素上可通过其 idouterHTML 属性来标识它们

l5tcr1uw

l5tcr1uw8#

我支持拍堆快照的建议,它们在检测内存泄漏方面非常出色,chrome在快照方面做得非常出色。
在我的学位研究项目中,我构建了一个交互式Web应用程序,该应用程序必须生成大量以“层”形式构建的数据,其中许多层将在UI中被“删除”,但由于某种原因,内存没有被释放。使用快照工具,我能够确定JQuery一直在保持对该对象的引用,(源代码是当我试图触发一个.load()事件时,该事件在超出范围的情况下仍保留引用)。这是一个非常有用的工具,当您使用其他人的库时,您会遇到延迟引用阻止GC执行其工作的问题。
编辑:提前计划要执行的操作以最大限度地减少快照所需的时间,假设可能导致问题的原因,并测试每个场景,在之前和之后制作快照,这也是很有用的。

相关问题