使用 Chrome 查找 JavaScript 内存泄漏
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19621074/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Finding JavaScript memory leaks with Chrome
提问by EleventyOne
I've created a very simple test case that creates a Backbone view, attaches a handler to an event, and instantiates a user-defined class. I believe that by clicking the "Remove" button in this sample, everything will be cleaned up and there should be no memory leaks.
我创建了一个非常简单的测试用例,它创建一个 Backbone 视图,将一个处理程序附加到一个事件,并实例化一个用户定义的类。我相信通过单击此示例中的“删除”按钮,所有内容都将被清除,并且应该没有内存泄漏。
A jsfiddle for the code is here: http://jsfiddle.net/4QhR2/
代码的 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();
However, I am unclear how to use Google Chrome's profiler to verify that this is, in fact, the case. There are a gazillion things that show up on the heap profiler snapshot, and I have no idea how to decode what's good/bad. The tutorials I've seen on it so far either just tell me to "use the snapshot profiler" or give me a hugely detailed manifesto on how the entire profiler works. Is it possible to just use the profiler as a tool, or do I really have to understand how the whole thing was engineered?
但是,我不清楚如何使用 Google Chrome 的分析器来验证事实确实如此。堆分析器快照上显示了无数的东西,我不知道如何解码什么是好/坏。到目前为止,我在上面看到的教程要么只是告诉我“使用快照分析器”,要么就整个分析器的工作原理提供了非常详细的宣言。是否可以仅将分析器用作工具,还是我真的必须了解整个过程是如何设计的?
EDIT:Tutorials like these:
编辑:像这样的教程:
Are representative of some of the stronger material out there, from what I've seen. However, beyond introducing the concept of the 3 Snapshot Technique, I find they offer very little in terms of practical knowledge (for a beginner like me). The 'Using DevTools' tutorial doesn't work through a real example, so its vague and general conceptual description of things aren't overly helpful. As for the 'Gmail' example:
据我所见,代表了一些更强大的材料。然而,除了介绍3 Snapshot Technique的概念之外,我发现它们提供的实践知识很少(对于像我这样的初学者)。'Using DevTools' 教程没有通过一个真实的例子,所以它对事物的模糊和一般概念性描述并没有太大帮助。至于“Gmail”示例:
So you found a leak. Now what?
Examine the retaining path of leaked objects in the lower half of the Profiles panel
If the allocation site cannot be easily inferred (i.e. event listeners):
Instrument the constructor of the retaining object via the JS console to save the stack trace for allocations
Using Closure? Enable the appropriate existing flag (i.e. goog.events.Listener.ENABLE_MONITORING) to set the creationStack property during construction
所以你发现了一个泄漏。怎么办?
在 Profiles 面板的下半部分检查泄漏对象的保留路径
如果无法轻易推断分配站点(即事件侦听器):
通过 JS 控制台检测保留对象的构造函数以保存分配的堆栈跟踪
使用闭包?启用适当的现有标志(即goog.events.Listener.ENABLE_MONITORING)以在构造期间设置creationStack 属性
I find myself more confused after reading that, not less. And, again, it's just telling me to dothings, not howto do them. From my perspective, all of the information out there is either too vague or would only make sense to someone who already understood the process.
读完之后,我发现自己更加困惑,而不是更少。并再次,它只是告诉我做的事情,而不是如何做这些事。从我的角度来看,所有的信息要么太模糊,要么只对已经了解流程的人有意义。
Some of these more specific issues have been raised in @Jonathan Naguin's answerbelow.
以下@Jonathan Naguin 的回答中提出了其中一些更具体的问题。
回答by Jonathan Naguin
A good workflow to find memory leaks is the three snapshottechnique, first used by Loreena Lee and the Gmail team to solve some of their memory problems. The steps are, in general:
查找内存泄漏的一个很好的工作流程是三快照技术,Loreena Lee 和 Gmail 团队首先使用它来解决他们的一些内存问题。大致步骤如下:
- Take a heap snapshot.
- Do stuff.
- Take another heap snapshot.
- Repeat the same stuff.
- Take another heap snapshot.
- Filter objects allocated between Snapshots 1 and 2 in Snapshot 3's "Summary" view.
- 拍摄堆快照。
- 做东西。
- 拍摄另一个堆快照。
- 重复同样的事情。
- 拍摄另一个堆快照。
- 在快照 3 的“摘要”视图中筛选在快照 1 和 2 之间分配的对象。
For your example, I have adapted the code to show this process (you can find it here) delaying the creation of the Backbone View until the click event of the Start button. Now:
对于您的示例,我修改了代码以显示此过程(您可以在此处找到)将 Backbone 视图的创建延迟到“开始”按钮的单击事件。现在:
- Run the HTML (saved locally of using this address) and take a snapshot.
- Click Start to create the view.
- Take another snapshot.
- Click remove.
- Take another snapshot.
- Filter objects allocated between Snapshots 1 and 2 in Snapshot 3's "Summary" view.
- 运行 HTML(使用此地址保存在本地)并拍摄快照。
- 单击开始以创建视图。
- 再拍一张。
- 单击删除。
- 再拍一张。
- 在快照 3 的“摘要”视图中筛选在快照 1 和 2 之间分配的对象。
Now you are ready to find memory leaks!
现在您已准备好查找内存泄漏!
You will notice nodes of a few different colors. Red nodes do not have direct references from Javascript to them, but are alive because they are part of a detached DOM tree. There may be a node in the tree referenced from Javascript (maybe as a closure or variable) but is coincidentally preventing the entire DOM tree from being garbage collected.
您会注意到几种不同颜色的节点。红色节点没有来自 Javascript 的直接引用,但它们是活的,因为它们是分离的 DOM 树的一部分。Javascript 引用的树中可能有一个节点(可能作为闭包或变量),但巧合的是阻止了整个 DOM 树被垃圾收集。
Yellow nodes however do have direct references from Javascript. Look for yellow nodes in the same detached DOM tree to locate references from your Javascript. There should be a chain of properties leading from the DOM window to the element.
然而,黄色节点确实有来自 Javascript 的直接引用。在同一个分离的 DOM 树中查找黄色节点以定位来自您的 Javascript 的引用。应该有一个从 DOM 窗口到元素的属性链。
In your particular you can see a HTML Div element marked as red. If you expand the element you will see that is referenced by a "cache" function.
在您的特殊情况下,您可以看到一个标记为红色的 HTML Div 元素。如果您展开该元素,您将看到它被“缓存”函数引用。
Select the row and in your console type $0, you will see the actual function and location:
选择该行并在您的控制台中键入 $0,您将看到实际的功能和位置:
>function cache( key, value ) {
return value;
}
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
This is where your element is being referenced. Unfortunally there is not much you can do, it is a internal mechanism from jQuery. But, just for testing purpose, go the function and change the method to:
这是您的元素被引用的地方。不幸的是,您无能为力,它是 jQuery 的内部机制。但是,仅出于测试目的,请转到函数并将方法更改为:
console.log = function() {};
console.warn = console.log;
console.error = console.log;
Now if you repeat the process you will not see any red node :)
现在,如果您重复该过程,您将看不到任何红色节点:)
Documentation:
文档:
回答by Rick Suggs
Here's a tip on memory profiling of a jsfiddle: Use the following URL to isolate your jsfiddle result, it removes all of the jsfiddle framework and loads only your result.
这是有关 jsfiddle 内存分析的提示:使用以下 URL 隔离您的 jsfiddle 结果,它会删除所有 jsfiddle 框架并仅加载您的结果。
http://jsfiddle.net/4QhR2/show/
http://jsfiddle.net/4QhR2/show/
I was never able to figure out how to use the Timeline and Profiler to track down memory leaks, until I read the following documentation. After reading the section entitled 'Object allocation tracker' I was able to use the 'Record Heap Allocations' tool, and track some some Detached DOM nodes.
在阅读以下文档之前,我一直无法弄清楚如何使用 Timeline 和 Profiler 来追踪内存泄漏。在阅读了标题为“对象分配跟踪器”的部分后,我能够使用“记录堆分配”工具,并跟踪一些分离的 DOM 节点。
I fixed the problem by switching from jQuery event binding, to using Backbone event delegation. It's my understanding that newer versions of Backbone will automatically unbind the events for you if you call View.remove()
. Execute some of the demos yourself, they are set up with memory leaks for you to identify. Feel free to ask questions here if you still don't get it after studying this documentation.
我通过从 jQuery 事件绑定切换到使用 Backbone 事件委托来解决这个问题。我的理解是,如果您调用View.remove()
. 自己执行一些演示,它们设置了内存泄漏供您识别。如果您在阅读本文档后仍然没有明白,请随时在此处提问。
https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling
https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling
回答by Konstantin Dinev
Basically you need to look at the number of objects inside your heap snapshot. If the number of objects increases between two snapshots and you've disposed of objects then you have a memory leak. My advice is to look for event handlers in your code which do not get detached.
基本上,您需要查看堆快照中的对象数量。如果两个快照之间的对象数量增加并且您已经处理了对象,那么您就会发生内存泄漏。我的建议是在您的代码中寻找不会分离的事件处理程序。
回答by u911838
There is an introduction video from Google, which will be very helpful to find JavaScript memory leaks.
有谷歌的介绍视频,对查找JavaScript内存泄漏很有帮助。
回答by bennidi
You also might want to read :
您可能还想阅读:
http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/
http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/
It explains the use of the chrome developer tools and gives some step-by-step advices on how to confirm and locate a memory leak using heap snapshot comparison and the different hep snapshot views available.
它解释了 chrome 开发人员工具的使用,并就如何使用堆快照比较和可用的不同 hep 快照视图确认和定位内存泄漏提供了一些分步建议。
回答by Robert Falkén
You could also look at the Timeline tab in developer tools. Record the usage of your app and keep an eye on the DOM Node and Event listener count.
您还可以查看开发人员工具中的“时间轴”选项卡。记录您的应用程序的使用情况并密切关注 DOM 节点和事件侦听器计数。
If the memory graph would indeed indicate a memory leak, then you can use the profiler to figure out what is leaking.
如果内存图确实表明内存泄漏,那么您可以使用分析器找出泄漏的内容。
回答by ProgrammerInProgress
I second the advice to take a heap snapshot, they're excellent for detecting memory leaks, chrome does an excellent job of snapshotting.
我第二个建议是拍摄堆快照,它们非常适合检测内存泄漏,chrome 在快照方面做得非常出色。
In my research project for my degree I was building an interactive web application that had to generate a lot of data built up in 'layers', many of these layers would be 'deleted' in the UI but for some reason the memory wasn't being deallocated, using the snapshot tool I was able to determine that JQuery had been keeping a reference on the object (the source was when I was trying to trigger a .load()
event which kept the reference despite going out of scope). Having this information at hand single-handedly saved my project, it's a highly useful tool when you're using other people's libraries and you have this issue of lingering references stopping the GC from doing its job.
在我的学位研究项目中,我正在构建一个交互式 Web 应用程序,该应用程序必须生成在“层”中构建的大量数据,其中许多层将在 UI 中“删除”,但由于某种原因,内存没有被解除分配,使用快照工具我能够确定 JQuery 一直在对象上保留一个引用(源是当我试图触发一个.load()
事件时,尽管超出了范围,但仍然保留了引用)。手头的这些信息单独保存了我的项目,当您使用其他人的库时,它是一个非常有用的工具,并且您遇到了拖延引用阻止 GC 执行其工作的问题。
EDIT: It's also useful to plan ahead what actions you're going to perform to minimize time spent snapshotting, hypothesize what could be causing the problem and test each scenario out, making snapshots before and after.
编辑:提前计划您将要执行的操作以最大限度地减少快照花费的时间,假设可能导致问题的原因并测试每个场景,在之前和之后制作快照也很有用。
回答by Jimmy Thomsen
A couple of important notes in regards to identifying memory leaks using Chrome Developer tools:
关于使用 Chrome 开发者工具识别内存泄漏的几个重要注意事项:
1) Chrome itself has memory leaks for certain elements such as password and number fields. https://bugs.chromium.org/p/chromium/issues/detail?id=967438. Avoid using those while debugging as they polute your heap snapshot when searching for detached elements.
1) Chrome 本身对于某些元素(例如密码和数字字段)存在内存泄漏。https://bugs.chromium.org/p/chromium/issues/detail?id=967438。避免在调试时使用它们,因为它们在搜索分离元素时会污染您的堆快照。
2) Avoid logging anythingto the browser console. Chrome will not garbage collect objects written to the console, hence affecting your result. You can suppress output by placing the following code in the beginning of you script/page:
2) 避免将任何内容记录到浏览器控制台。Chrome 不会垃圾收集写入控制台的对象,从而影响您的结果。您可以通过将以下代码放在脚本/页面的开头来抑制输出:
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
3) Use heap snapshots and search for "detach" to identify detached DOM elements. By hovering objects, you get access to all the properties including idand outerHTMLwhich may help identify each element.
If the detached elements are still too generic to recognize, assign them unique IDs using the browser console prior to running your test, e.g.:
3) 使用堆快照并搜索“分离”来识别分离的 DOM 元素。通过悬停对象,您可以访问所有属性,包括id和outerHTML,这可能有助于识别每个元素。
如果分离的元素仍然太通用而无法识别,请在运行测试之前使用浏览器控制台为它们分配唯一的 ID,例如:
Now, when you identify a detached element with, lets say id="AutoId_49", reload your page, execute the snippet above again, and find the element with id="AutoId_49" using the DOM inspector or document.querySelector(..). Naturally this only works if your page content is predictable.
现在,当您使用 id="AutoId_49" 识别分离的元素时,重新加载您的页面,再次执行上面的代码段,然后使用 DOM 检查器或 document.querySelector(..) 找到具有 id="AutoId_49" 的元素. 当然,这仅在您的页面内容可预测时才有效。
How I run my tests to identify memory leaks
我如何运行测试以识别内存泄漏
1) Load page (with console output suppressed!)
1)加载页面(控制台输出被抑制!)
2) Do stuff on page that could result in memory leaks
2)在页面上做可能导致内存泄漏的事情
3) Use Developer Tools to take a heap snapshot and search for "detach"
3)使用开发者工具拍摄堆快照并搜索“分离”
4) Hover elements to identify them from their idor outerHTMLproperties
4) 悬停元素以从它们的id或outerHTML属性中识别它们