查找分离的DOM树内存泄漏 [英] Locating detached DOM tree memory leak

查看:134
本文介绍了查找分离的DOM树内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用Knockout构建的非常大的单页Web应用程序中诊断分离的DOM树内存泄漏时遇到了问题。



我调整了应用程序将一个虚拟的 FooBar 对象附加到一个特定的HTML按钮元素,该元素在用户移动到应用程序的另一个页面时应该被垃圾收集。通过使用Chrome的堆快照功能,我可以看到旧的 FooBar 实例(应该已被GC化)仍然可以从<$ (大)分离的DOM树中的c $ c> HTMLButtonElement 。
$ b

通过保留树追踪引用面板上,我沿着链条从GC根部开始减小距离。然而,在某些时候,我的搜索在距离根节点4(在这种情况下)达到了死胡同!保留树根本不报告对该节点的引用,但不知何故它知道它是从GC根节点的四个步骤。



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

  v HTMLButtonElement中的foobar 10 
v [4928]在分离的DOM树/ 5643条目中9
v原生HTMLOptionElement 8
v [0]数组7
v mappedNodes 6
v [870]数组5
v itemsToProcess in system /上下文4函数itemMovedOrRetained()中的
上下文
函数中的上下文callCallback()

保留树在距离3或以上不显示参考。



任何人都可以向我解释这个吗?我希望能够引用链回到JavaScript应用程序代码的有问题的部分 - 但是这让我很难过!

解决方案

首先 - 不要使用 delete 作为建议的注释之一。设置对 null 的引用是处理事情的正确方法。 delete 打破了隐藏的课程。要自己查看,请运行我的示例,从 https://github.com/naugtur/js-memory-演示

Rafe,你在profiler中看到的内容通常很难理解。您在这里发布的内容看起来很奇怪,可能是您应用程序之外的漏洞或内存泄漏(浏览器也会泄露),但是如果没有运行您的应用程序,很难说清楚。您的保留树结束于一个函数的上下文中,并且可以通过对该函数或共享上下文的其他函数的引用来保留它。探查器可能太复杂,无法正确显示它。



我可以帮你找出问题所在。

首先,进入devtools中的Timeline选项卡并使用它观察发生泄漏的时刻。只选择内存分配并开始记录。浏览一个你期望泄漏的场景。仍然是蓝色的酒吧是泄漏。您可以在时间轴中选择周围环境,并专注于其保留树。分离的dom树中最有趣的元素是红色的 - 它们是从外部引用的。其余的被保留,因为无论树中的元素被引用,它都会引用其他所有内容( x.parentNode

如果您需要更多详细信息,可以在探查器中采用多个快照,以便在泄漏原因(您在时间轴中找到的 - 现在知道导致该问题的确切操作)之前和之后都有快照。然后您可以比较探查器中的那些 - 有一个比较视图。这比其他人更容易理解。

您还可以从探查器保存堆快照并将它们发布到网上,以便我们可以看一看。他们每个人都有一个保存链接在左边的列表中。






分析内存很难,实际上需要一些练习并了解这些工具。
您可以在我的演讲中练习一些例子:

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



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

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



更新后的链接: https://开发人员。 google.com/web/tools/profile-performance/memory-problems/memory-diagnosis


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

I've tweaked the app to attach a dummy FooBar object 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 snapshot function, I can see that an old FooBar instance (which should have been GC'ed) is still reachable from its HTMLButtonElement in a (large) detached DOM tree.

Tracing the references via the retaining tree panel, 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.

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.

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!

解决方案

First of all - do not use delete as one of the comments suggested. Setting a reference to null is the right way to dispose of things. delete breaks the "hidden class". To see it yourself, run my examples from 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.

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)

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

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

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

这篇关于查找分离的DOM树内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆