我在主要使用Knockout构建的非常大的单页Web应用程序中诊断分离的DOM树内存泄漏时遇到了问题。
我已经调整了应用程序,将虚拟FooBar
对象附加到特定的HTML按钮元素,当用户移动到应用程序的不同“页面”时,该元素应该被垃圾收集。使用Chrome的堆快照功能,我可以看到旧的FooBar
实例(应该已经过GC)仍然可以从(大)分离的HTMLButtonElement
中找到DOM树。
通过保留树面板跟踪引用,我跟踪链接从GC根目录减少距离。但是,在某些时候,我的搜索在与根的节点距离4处达到了死胡同(在这种情况下)!保留树根本不报告对此节点的引用,但不知何故知道它是GC根目录的四个步骤。
以下是保留树的一部分让我感到困惑(右边的数字是距离根部的距离):
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()
保留树不会在距离3或以上显示参考。
任何人都可以向我解释这个吗?我希望我能够跟随参考链回到JavaScript应用程序代码中令人讨厌的部分 - 但这让我受阻了!
答案 0 :(得分:13)
首先 - 不要使用delete
作为建议的评论之一。设置对null
的引用是处理事物的正确方法。 delete
打破了隐藏的类"。要自己查看,请从https://github.com/naugtur/js-memory-demo
Rafe,您在分析器中看到的内容通常很难理解。你在这里发布的这个位看起来很奇怪,可能是你的应用程序之外的一个bug或内存泄漏(浏览器泄漏),但是如果没有运行你的应用程序,很难说。保留树在函数的上下文中结束,并且可以通过对该函数的引用或共享上下文的某个其他函数来保留。对于探查器来说,正确地可视化它可能太复杂了。
我可以帮助您查明问题。
首先,转到devtools中的Timeline选项卡,然后使用它来观察泄漏发生的那一刻。仅选择内存分配并开始录制。浏览一下您希望泄漏的场景。保持蓝色的条纹是泄漏。您可以在时间轴中选择它们的周围环境,并专注于它们的保留树。分离的dom树中最有趣的元素是红色的 - 它们是从外部引用的。其余的保留,因为引用了树中的任何元素,它引用了其他所有元素(x.parentNode
)
如果您需要更多详细信息,可以在探查器中拍摄多个快照,以便在泄漏原因之前和之后获得快照(您在时间轴中找到了快照 - 您现在知道导致它的确切操作) 。然后,您可以比较分析器中的那些 - 那里有一个"比较"视图。这比其他人更容易理解。
您还可以从分析器中保存堆快照并在线发布,因此我们可以看看。左侧列表中的每个都有一个保存链接。
分析内存很难,实际上需要一些练习和对工具的理解。 你可以从我的演讲中练习一些例子:
http://naugtur.pl/pres/mem.html#/5/2
但是使用内存分析器的真正完整指南是这个doc:
https://developer.chrome.com/devtools/docs/javascript-memory-profiling#looking_up_color_coding
更新了链接: https://developers.google.com/web/tools/profile-performance/memory-problems/memory-diagnosis