1. 程式人生 > >馴服獨角獸:用Chrome DevTools做輕鬆的javascript記憶體分析

馴服獨角獸:用Chrome DevTools做輕鬆的javascript記憶體分析

“The Unicorn has a special ability to help it's master when in trouble. When tamed, they will not attack humans or tamed creatures, but will attack anything else with negative karma"

獨角獸在遇到麻煩事擁有一種特殊能力去幫助它的主人”被馴服後,他們不會攻擊人類或馴服的動物,但會攻擊其他不好的事物。

The DevTools Heap Profiler and Timeline Memory view can be used to diagnose memory leaks on both desktop and mobile (via 

remote debugging). When you first use them however, they can feel like a little like a black box, requiring some trial and error to correctly master.

DevTools堆分析和時間軸記憶體檢視可用於桌上型電腦和移動裝置(通過遠端除錯)診斷記憶體洩漏。當你第一次使用它們,他們可以感覺好像有點像一個黑盒子,需要反覆嘗試正確地掌握。

In this article, I’ll walk you through how to tame these tools with answers to some common FAQs to help save you time when memory profiling your apps.

在這篇文章中,我將帶您通過一些常見的常見問題的解答瞭解怎樣幫你使用工具,幫助您在記憶體分析您的應用程式時節省時間。

Understanding the Unicorn

瞭解獨角獸

A garbage collector (such as the one in V8) needs to be able to locate objects in your application which are live as well as those which are considered dead (garbage) andcannot be reached. If garbage collection (GC) misses dead objects due to logical errors in your JavaScript, memory consumed by these objects can’t be reclaimed and can end up making your application slow over time.

一個垃圾收集器(如V8中的GC)需要能夠找到您的應用程式中的物件,這些物件是活的,但是其實應該是 “死” 的(垃圾) ,因為他們不能達到。如果垃圾收集(GC )由於你的JavaScript邏輯錯誤而錯過回收死物件,這些物件所消耗的記憶體將不能被回收,最終可以使您的應用程式變慢。

This often happens when you’ve written your code in such a way that variables and event listeners you don’t require for long are still referenced by some code that no longer has a need to keep those references and thus they can’t be correctly cleanedup by GC

這種情況經常發生,當你寫你的程式碼時,一些你很長時間不再需要的變數和事件偵聽器他們仍然被一些程式碼引用,但是這些程式碼不再被引用,而他們卻無法正確的被GC清理。

Remember to check and nullify variables that contain references to DOM elements which may be getting updated/destroyed during the lifecycle of your app. Check object properties which may reference other objects (or other DOM elements). Be sure to keep an eye on variable caches which may accumulate over time. 

記住要檢查並且作廢那些屬於DOM元素並且在其生命週期中可能被更新/銷燬的物件。檢查可能會引用其他物件(或其他的DOM元素)的物件屬性 。一定要留意那些可能會隨著時間的推移而積累快取的變數。

Q: I’ve been told to use the Heap Profiler and Timeline Memory view for detecting memory leaks. What tool should be used first?

問:我已經被告知使用Heap Profiler和Timeline Memory view用於檢測記憶體洩漏。應首先使用什麼樣的工具?

The Timeline. Use it to diagnose excessive memory usage when you first notice your page has slowed down after extended use. Slowdown was once a classic symptom of a memory leak but it could also be something else – maybe you have a paint or network bottleneck in your page, so make sure to fix the real issue in your page.

時間軸。用它來診斷記憶體過度使用,長時間使用後當你第一次看到你的頁面已經放緩。放緩是一次經典的記憶體洩漏的症狀,但它也可能是別的問題 - 也許你在你的網頁上有渲染或網路瓶頸,所以一定要解決在您的網頁上真正的問題。

To diagnose whether memory is the issue, go to the Timeline panel and Memory view. Hit the record button and interact with your application, repeating any steps you feel may be causing a leak. Stop the recording. The graph you see will display the memory allocated to your application. If it happens to be consuming an increasing amount of this over time (without ever dropping), it’s an indication you may have a memory leak.

要診斷是否記憶體是問題,去時間軸面板和記憶體檢視。按下錄音鍵,並與應用程式互動,重複您覺得可能會造成洩漏的所有步驟。停止錄音。你看到的圖形會顯示您的應用程式分配的記憶體。如果它正好是消耗這個隨著時間的推移越來越多(沒有下降),這表明你可能有一個記憶體洩漏。

The profile for a healthy application should look more like a sawtooth curve as memory is allocated then freed when the garbage collector comes in. There’s nothing to worry about here – there’s always going to be a cost of doing business in JavaScript an even an empty requestAnimationFrame will cause this type of sawtooth, you can’t avoid it. Just ensure it’s not sharp as that’s an indication a lot of allocations are being made, which can equate to a lot of garbage on the other side.

一個健康的應用的輪廓應該看起來更像是一個鋸齒形曲線,因為開始有記憶體的分配,然後由於垃圾回收的介入釋放。不要擔心 - JavaScript做業務處理總是會消耗的,甚至一個空requestAnimationFrame會引起這種型別的鋸齒,你無法避免它。只要確保它不是尖銳的,因為這是一個跡象正在分配很多,等同於大量的垃圾在另一側。

It's the rate of increase in the steepness of this curve that you need to keep an eye on.There is also a DOM node counter, Document counter and Event listener count in the Memory view which can be useful during diagnosis. DOM nodes use native memory and do not directly affect the JavaScript memory graph.

這是增加的速率在陡峭的曲線,你需要留意。記憶體檢視有診斷過程中是有用的DOM節點計數器,文件計數器和事件監聽計算在。 DOM節點使用本地記憶體,並不會直接影響的JavaScript記憶體圖。

Once you suspect you have a memory leak, the Heap profiler can be used to discover the source of the leak.

一旦懷疑你有記憶體洩漏,Heap profiler可以用來發現洩漏源。

Q: What workflow do you use for diagnosing memory leaks in the Heap Profiler?

問:在Heap Profiler中診斷記憶體洩漏你用什麼樣的工作流程?

Diagnosing issues with memory can be tricky, but the best place to start is the Heap profiler's Summary view. Go to Profiles and take a heap snapshot before an intensive part of your code kicks in and then one more after this code has been allowed to run for a while. Repeat. You can then compare the snapshots using the Summary (and other) views to confirm what allocations have been made along with their impact on memory.

診斷記憶體問題可能會非常棘手,但開始最好的地方是Heap profiler的Summary檢視。進入Profiles,並在你程式碼密集部分載入之前獲取heap snapshot,這段程式碼執行一段時間後再獲取一個heap snapshot。重複。然後,您可以使用Summary(和其他)的檢視確認已作出什麼樣的分配連同對記憶體的影響。

A good workflow for this is the "three snapshot" technique, first used by Loreena Lee and the GMail team to solve some of their memory problems. The steps for this are:

“three snapshot”技術是一個很好的工作流程,第一次用於Loreena 和Gmail團隊來解決一些他們的記憶體問題。這樣的步驟是:

  1. Open the DevTools > Profiles  開啟DevTools > Profiles
  2. Take a heap snapshot 獲取heap snapshot
  3. Perform an action in your app that you think is causing leaks 執行一個你認為在你的應用程式導致洩漏的動作
  4. Take a heap snapshot 獲取heap snapshot
  5. Repeat the same stuff once more 再次重複同樣的操作
  6. Take a final heap snapshot 獲取最終的heap snapshot
  7. Select the most recent snapshot taken 選擇最近的snapshot
  8. At the bottom of the window, find the drop-down that says "All objects" and switch this to "Objects allocated between snapshots 1 and 2". (You can also do the same for 2 and 3 if needed) 在視窗的底部,找到下拉“All objects”,切換到“Objects allocated between snapshots 1 and 2”(你也可以切換到“Objects allocated between snapshots 2 and 3”)
  9. In the view you'll see a list of leaked objects that are still hanging around. You can select one to see what is being retained in its retaining tree. 在檢視中,你會看到仍然停留的洩漏物件的列表。您可以選擇一個,看看在其關係樹中有什麼被保留

To fix, you'll want to find where in your code you're still retaining these references and properly dispose of them. If there are no leaks the Summary view should be empty.

若要解決此問題,你會想找到你在你的程式碼中仍保留這些引用的地方,並妥善處置。如果沒有洩漏摘要檢視應該是空的

Note: it sometimes makes sense to do a warm-up action before taking the first heap snapshot as there are cases where you might be doing lazy initialization for global variables on the first invocation. This is mostly a pro-tip for advanced use-cases, but leaving it here in case it is of help.

注意:它有時感覺像第一次快照之前做熱身動作,就和你可能會做延遲初始化的全域性變數一樣。這主要是一個功能給先進的使用者,但在這裡留下以防它是有用的

Q: I noticed a number of DOM nodes in the heap snapshot where some are highlighted in red and indicated as a "Detached DOM tree" whilst others are yellow. What does this mean?

問:我注意到在一個堆快照裡以紅色突出顯示的很多DOM節點,並表示作為一個“Detached DOM tree”,而其他的是黃色的。這是什麼意思?

You'll notice nodes of a few different colors. Red nodes do not have direct references from JavaScript to them, but are alive because they’re 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 (e.g window.foo.bar[2].baz).

黃節點也有直接從JavaScript引用。看黃色在同一個分離的DOM樹節點定位您的JavaScript的引用。從DOM視窗到元素應該有一個鏈。

Q: What do the Shallow and Retained Size columns represent and what are the differences between them?

問:Shallow和Retained Size表示什麼,它們之間的區別是什麼?

So, objects can be kept in memory (be alive) in two different ways – either directly by another alive object (window and document are always alive objects) or implicitly by holding references from native part of the renderer (like DOM objects). The latter is what ends up preventing these objects from being disposed by GC automatically, causing leaks. The size of memory held by an object itself is known as the shallow size (generally, arrays and strings have larger shallow sizes).

因此,物件可以儲存在記憶體(活著)兩種不同的方式 - 無論是直接由另一個活著的物件(視窗和文件物件總是活著的物件)或隱式引用渲染器的本地部分(如DOM物件)。後者是最終防止這些物件被GC自動處理,導致洩漏。一個目的本身持有的記憶體的大小被稱shallow size(通常,陣列和字串淺的shallow sizes較大)

An object of any size can hold a ton of memory if it prevents other objects from being disposed. The size of memory that can be freed once an object is deleted (and this its dependents made no longer reachable) is called the retained size.

任何大小的一個物件,可容納一噸的記憶體,如果它可以防止其他物件被處理。一旦一個物件被刪除記憶體的大小,可以釋放,(和其家屬不再可達)被稱為retained size

Q: There's a lot of data in the constructor and retained views. Where should I start digging into to discover if I have a leak?

問:有很多資料在建構函式及retained檢視中。我應該從哪裡開始挖掘發現,是否我有一個洩漏?

It's generally a good idea to begin investigation from the first object retained in your tree as retainers are sorted by distance (well, distance to the window).

這通常是一個好主意,開始調查的第一個物件保留在你的樹,保留者按距離(距離視窗)進行排序

The object retained with the shortest distance is usually your first candidate for causing a memory leak.

用最短的保留的物件,通常是你的第一個候選者造成記憶體洩漏

Q: What's the difference between the different Summary, Comparison, Dominators and Containment views?

問: Summary, Comparison, Dominators 和Containment views之間的區別是什麼?

You may get some mileage by switching between the different data views available at the bottom of the screen.

你可能會得到一些行駛里程通過螢幕底部的不同的資料檢視之間切換。

  • Summary view helps you hunt down objects (and their memory use) based on type grouped by constructor name. This view is particularly helpful for tracking down DOM leaks.

    Summar檢視,可以幫助你追捕物件(和記憶體使用)根據構造器名稱型別分組。這檢視追查DOM洩漏是特別有用的

  • Comparison view helps you track down memory leaks, by displaying which objects have been correctly cleaned up by the garbage collector. Generally used to record and compare two (or more) memory snapshots of before and after an operation. The idea is that inspecting the delta in freed memory and reference count lets you confirm the presence and cause of a memory leak.

    Comparison檢視,可以幫助您跟蹤記憶體洩漏,通過顯示哪些物件已正確地清理被垃圾收集器。一般用於記錄和比較兩個(或更多)的操作之前和之後的記憶體快照。這個想法是,檢查釋放的記憶體的三角洲和引用數,讓你確認是否存在,並導致記憶體洩漏

  • Containment view provides a better view of object structure, helping us analyse objects referenced in the global namespace (i.e. window) to find out what is keeping them around. It lets you analyse closures and dive into your objects at a low level.

    Containment檢視提供了更好的視野。幫助我們分析在全域性名稱空間(即視窗) ,找出物件引用。它可以讓你在一個較低的水平,分析閉包和研究你的物件

  • Dominators view helps confirm that no unexpected references to objects are still hanging around (i.e that they are well contained) and that deletion/garbage collection is actually working.

    Dominators檢視有助於確認,沒有物件的引用仍然徘徊(i.e 包含)並且刪除/垃圾收集實際上是工作著的。

Q: What do the various constructor (group) entries in the Heap profiler correspond to?

問:請問Heap profiler的各種constructor(組)條目對應什麼?

  • (global property) – intermediate objects between a global object (like 'window') and an object referenced by it. If an object is created using a constructor Person and is held by a global object, the retaining path would look like [global] > (global property) > Person. This contrasts with the norm, where objects directly reference each other. We have intermediate objects for performance reasons. Globals are modified regularly and property access optimisations do a good job for non-global objects aren't applicable for globals.
  • 全域性屬性) - 一個全域性物件(如'window' )和它引用的物件之間的中間物件。如果建立一個物件使用建構函式Person,被一個全域性物件引用,固定路徑會是什麼樣子[global] > (global property) > Person.。與此相反,物件直接引用對方。出於效能方面的考慮,我們有中間物件。定期修改全域性屬性訪問優化對非全域性物件很好,但並不適用於全域性
  • (roots) – The root entries in the retaining tree view are the entities that have references to the selected object. These can also be references created by the engine for its own purposes. The engine has caches which reference objects, but all such references are weak and won't prevent an object from being collected given that there are no truly strong references.
  • roots) - 根條目retaining tree檢視中所選物件的引用的實體。這些也可以由引擎為自己的目的建立的引用。引擎有快取引用物件,但所有這些引用是弱的,並不會防止被收集,有沒有真正的強引用物件
  • (closure) – a count of references to a group of objects through function closures
  • (閉包) - 對一組通過閉包函式的物件的引用計數
  • (array, string, number, regexp) – a list of object types with properties which reference an Array, String, Number or regular expression
  • (陣列,字串,數字,正則表示式) - 引用陣列,字串,數字或正則表示式的屬性物件型別的列表
  • (compiled code) – simply, everything related to compiled code. Script is similar to a function but corresponds to a <script> body. SharedFunctionInfos (SFI) are objects standing between functions and compiled code. Functions are usually have a context, while SFIs do not.
  •  (compiled code) - 簡單地說,一切和編譯後的程式碼相關。指令碼類似於一個函式,對應於一個< Script >的身體。 SharedFunctionInfos ( SFI )是在功能和編譯程式碼之間的物件。函式通常是有一個上下文, SFIS沒有
  • HTMLDivElement, HTMLAnchorElement, DocumentFragment etc – references to elements or document objects of a particular type referenced by your code.
  • HTMLDivElement , HTMLAnchorElement , DocumentFragment的等等 - 你的程式碼引用特定型別的元素或文件物件的引用。

Many of the other objects you may see were likely generated during the lifecycle of your code and can include event listeners as well as custom objects, like the controllers below:

你的程式碼的整個生命週期過程中可能產生許多您可能會看到其他物件可以包括事件偵聽器以及自定義物件,如下面的控制器:

Q: Is there anything I should be turning off in Chrome that might be influencing my figures?

問:有沒有什麼我應該關閉在Chrome中,可能會影響我的數字呢?

When performing any type of profiling using the Chrome DevTools, it is recommended that you either run in incognito mode with all extensions disabled or start Chrome with a custom user data directory (--user-data-dir=”…”).

當使用Chrome DevTools執行任何型別的分析,都建議,你要麼執行在隱身模式下的所有擴充套件禁用或啟動Chrome瀏覽器的自定義使用者資料目錄

Apps, extensions and even console logging can have an implicit impact on your figures and you want to keep them as reliable as possible.

應用程式,擴充套件,甚至控制檯日誌記錄可以有一個隱含的影響對您的資料,你想保持他們儘可能可靠