javascript 定位分离的 DOM 树内存泄漏

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21226646/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 20:26:11  来源:igfitidea点击:

Locating detached DOM tree memory leak

javascripthtmlgoogle-chromememory-leaks

提问by Rafe

I'm having trouble diagnosing a detached DOM tree memory leak in a very large single-page web app built primarily with Knockout.

在主要使用 Knockout 构建的非常大的单页 Web 应用程序中,我无法诊断分离的 DOM 树内存泄漏。

I've tweaked the app to attach a dummy FooBarobject to a particular HTML button element which should be garbage collected as the user moves to a different "page" of the app. Using Chrome's heap snapshotfunction, I can see that an old FooBarinstance (which should have been GC'ed) is still reachable from its HTMLButtonElementin a (large) detached DOM tree.

我已经调整了应用程序,将一个虚拟FooBar对象附加到一个特定的 HTML 按钮元素,当用户移动到应用程序的不同“页面”时,该元素应该被垃圾收集。使用 Chrome 的堆快照功能,我可以看到旧FooBar实例(应该已经被 GC 处理)仍然可以从它HTMLButtonElement的(大)分离的 DOM 树中访问。

Tracing the references via the retaining treepanel, I follow the chain taking decreasing distance from the GC root. However, at some point my search reaches a dead end at a node distance 4 from the root(in this case)! The retaining tree reports no references to this node at all, yet somehow knows it is four steps from the GC root.

通过保留树面板跟踪引用,我遵循与 GC 根的距离逐渐减小的链。但是,在某些时候,我的搜索在距根节点距离为 4 处(在这种情况下)到达死胡同!保留树根本没有报告对这个节点的引用,但不知何故知道它离 GC 根有四个步骤。

Here is the part of the retaining tree which has me puzzled (the numbers on the right are distances from the root):

这是保留树的一部分,让我感到困惑(右边的数字是到根的距离):

v foobar in HTMLButtonElement                                  10
  v [4928] in Detached DOM tree / 5643 entries                  9
    v native in HTMLOptionElement                               8
      v [0] in Array                                            7
        v mappedNodes                                           6
          v [870] in Array                                      5
            v itemsToProcess in system / Context                4
                context in function itemMovedOrRetained()
                context in function callCallback()

The retaining tree doesn't show the references here at distance 3 or above.

保留树不会在此处显示距离 3 或以上的引用。

Can anyone explain this to me? I was hoping I'd be able to follow the reference chain back up to the offending part of the JavaScript app code -- but this has my stymied!

任何人都可以向我解释这一点吗?我希望我能够按照参考链回到 JavaScript 应用程序代码的违规部分——但这让我受阻!

回答by naugtur

First of all - do not use deleteas one of the comments suggested. Setting a reference to nullis the right way to dispose of things. deletebreaks the "hidden class". To see it yourself, run my examples from https://github.com/naugtur/js-memory-demo

首先 - 不要delete用作建议的评论之一。设置引用null是处理事物的正确方法。delete打破“隐藏阶级”。要自己查看,请从https://github.com/naugtur/js-memory-demo运行我的示例

Rafe, the content you see in profiler is often hard to understand. The bit you posted here does seem odd and might be a bug or a memory leak outside of your application (browsers leak too), but without running your app it's hard to tell. Your retaining tree ends in a context of a function and it can be retained by a reference to that function or some other function sharing the context. It might be too complicated for the profiler to visualize it correctly.

Rafe,您在分析器中看到的内容通常很难理解。您在此处发布的内容看起来确实很奇怪,可能是您的应用程序之外的错误或内存泄漏(浏览器泄漏也是如此),但如果不运行您的应用程序,就很难判断。您的保留树以函数的上下文结束,可以通过引用该函数或共享上下文的其他函数来保留它。探查器要正确地将其可视化可能太复杂了。

I can help you pinpoint the problem though.

不过我可以帮你查明问题所在。

First, go to Timeline tab in devtools and use it to observe the moment your leak happens. Select only memory allocation and start recording. Go through a scenario that you expect to leak. The bars that remain blue are the leaks. You can select their surrounding in the timeline and focus on their retaining tree. The most interesting elements in detached dom trees are the red ones - they're referenced from the outside. The rest is retained because whatever element in a tree is referenced, it has references to everything else (x.parentNode)

首先,转到 devtools 中的 Timeline 选项卡并使用它来观察泄漏发生的时刻。仅选择内存分配并开始录制。经历一个你预计会泄露的场景。保持蓝色的条是泄漏。您可以在时间轴中选择它们的周围环境并专注于它们的保留树。分离的 dom 树中最有趣的元素是红色的——它们是从外部引用的。其余部分被保留,因为树中的任何元素被引用,它都有对其他所有元素的引用 ( x.parentNode)

If you need more details, you can take multiple snapshots in the profiler, so that you have a snapshot before and after the cause of the leak (that you found with the timeline - you now know the exact action that causes it). You can then compare those in the profiler - there's a "compare" view. which is more comprehensible than others.

如果您需要更多详细信息,您可以在分析器中拍摄多个快照,以便您在泄漏原因之前和之后都有一个快照(您通过时间线找到的 - 您现在知道导致泄漏的确切操作)。然后您可以在分析器中比较它们 - 有一个“比较”视图。这比其他人更容易理解。

You can also save your heap snapshots from the profiler and post them online, so we could take a look. There's a save link on each of them in the list to the left.

您还可以从分析器中保存堆快照并将其发布到网上,以便我们查看。左侧列表中的每个文件都有一个保存链接。



Profiling memory is hard and actually requires some practice and understanding of the tools. You can practice on some examples from my talk:

分析内存很难,实际上需要对工具进行一些练习和理解。你可以练习我演讲中的一些例子:

http://naugtur.pl/pres/mem.html#/5/2

http://naugtur.pl/pres/mem.html#/5/2

but the real complete guide to using memory profiler is this doc:

但是使用内存分析器的真正完整指南是这个文档:

https://developer.chrome.com/devtools/docs/javascript-memory-profiling#looking_up_color_coding

https://developer.chrome.com/devtools/docs/javascript-memory-profiling#looking_up_color_coding

Updated link:https://developers.google.com/web/tools/profile-performance/memory-problems/memory-diagnosis

更新链接:https : //developers.google.com/web/tools/profile-performance/memory-problems/memory-diagnosis