本系列文章的目的是通過我正在進行的工作,即為Chrome內存配置文件創建可視化工具,來記錄我對JavaScript性能的探索。我們將從當時創建這個項目的目的開始講起,然后深入了解Chrome用于表示內存堆的文件格式。
數月前,我曾有機會去聆聽了Casey Rosenthal一場關于直覺工程主題的精彩的演講。直覺工程是一種概念,這種概念是指人們在理解信息時變得更容易,而不需要再去仔細思考。像視覺、聲音和氣味這樣的東西,可以用閱讀一段文字或者解釋一個表格這么一小段時間來處理。
你能多快地了解這個應用服務器上發生了什么:

那相比這個呢:
新Relic錯誤分析展示圖
很顯然,在適當的抽象層面上對數據進行可視化表示可以更容易地了解實際發生的情況。Casey說一旦你接觸過這種抽象有一段時間,那么你就會形成一種直覺意識:“什么是正常的”。基線的變化是顯而易見的,這樣我們就可以利用人類的自然本能來進行模式識別,從而讓我們深入了解可能導致行為變化的因素。
這使得直覺工程成為設計診斷接口的絕佳平臺,特別是那些隨著時間而顯示數據的平臺。通過提供一個允許用戶開發直觀基準的接口,您可以讓用戶快速區分每條基準線之間的區別。
對直覺工程有一個大概了解之后,我很想在個人項目上嘗試一下。當時我正在研究一些JavaScript內存堆分析,試圖診斷應用程序占用內存的原因,并追蹤一些內存泄漏。
花費幾天時間研究接口之后:
Chrome內存文件監控器
我想也許我可以利用這個直覺工程的想法來構建一個更好的堆快照視圖。通過以可視化的方式呈現數據,我們可以快速了解內存分配的位置以及GC運行期間沒有清理的內容。在應用程序中開發直觀的基準可能很困難,但是隨著時間的推移,應用程序當然可以有一個視覺上的理解,可以讓我們快速識別重大事件或問題領域。
如果我想要以可視化的方式表示堆配置文件,我的步需要將導出的Chrome堆概要文件格式解析為可以看到的東西。堆配置文件格式巧妙地壓縮了JSON。它看起來像這樣:
摘錄自.heapprofile文件
這些文件非常大,通常是幾百兆字節。這種格式花了我很長一段時間才搞清楚,但是我終通過Chrome devtools進行了大量的試用和錯誤的方法,終于解決了這個問題。
內存表示為圖形,節點表示內存分配,edge表示對這些內存位置的引用。 樣本由時間戳和后分配的edge組成。 該文件的每行代表其類型的一個對象。 對象的字段在元數據中標識為node_fields。
一個節點可以通過其edge_count字段來確定它的邊。從個節點開始,edges遞增地分配給節點。因此,個節點的edge_count為8,由前8個edge引用。 第二個節點edge_count為17,擁有接下來的17個edges,依此類推。
對父節點的edges引用不是由ID完成的,而是通過數組中節點的起始元素的索引來完成。 類似地,時間樣本由樣本中分配的后一個edges的結束索引標記。 我花了很長時間才弄明白這一點,但好處是顯而易見的——它創建了一個的整數鍵,用于快速查找數組。不需要將整個圖解析為數據結構,以便確定有關單個edges或節點的數據。
對于任何給定的節點,有三個統計特別重要:
自身大小-僅由節點本身單獨存儲的內存大小。 非常大的內存中對象將具有較大的自身大小,比如長字符串、字典和具有大主體的函數。尋找具有非常大的自身大小的對象通常是減少應用程序內存占用的步。
保留大小-對象的自身大小加上所有被釋放的對象的大小應該被刪除。 在數學上,這個節點主導的任何節點(更多的是在一秒鐘內)被添加到其保留的大小。 具有非常大的保留尺寸的對象如果被清理,將釋放大量內存,即使它們沒有特別大的自身尺寸。
其保留者的身份 - 節點的任何邊緣都提供了將內容保存在內存中的信息。 一旦你確定了一組呈現問題的節點,這是重要的,因為它表明在內存中保留這些節點是什么。 保留者還可以為您提供關于標簽不良節點的真實性質的有用線索。
個和第三個方法的作用是微不足道的-因為節點自身的大小是在定義節點時給出的,通過構建圖形,您可以輕松找到節點的保留者。 然而,計算節點的保留大小并不是那么簡單。 要計算它,我們必須首先擁有節點占主導地位的所有節點的列表。
如果遍歷到根節點時,所有路徑必須經過N,就認為節點就被節點N所主導。站在內存的角度,這就意味著如果你刪除了從N到節點的引用,那么它就不會再有引用了,在下一次傳遞中會被垃圾回收站給回收掉。為了從圖中生成一系列的支配節點,我們就需要創建支配樹了。
有一種算法很容易就可以構建這樣一種樹,稱為Tarjan-Lengauer算法。這個算法是相當有技術性的,我開始擼起袖子,還有很多疑問,就嘗試用JavaScript去實現。我思考了一下并且稍微挖掘了一下,然后我就意識到,在我面前的是一個多么好的已經實現的開源算法。
我知道Chrome devtools正在構建一個支配樹,因為它們從文件中加載堆快照時,可以輕松地將其作為進程消息之一進行閃爍。快速地訪問Chromium源代碼后,讓我想到了HeapSnapshot.js和朋友。我意識到——如果Chrome有一個開源的、可以用于生產的應用程序,那么我為什么還要自己去弄呢?
我現在很高興地跟大家說,它運行得很好。我能夠提取HeapSnapshot模塊并在瀏覽器中進行旋轉。我必須填補一些東西,但是devtools模塊的目的是用來掛起一個全局對象,這樣就可以輕易地進行抽取—只需提供您自己的、正確命名的全局對象,確保它們擁有所需的任何實用程序,并且它們將完成其余的工作。。
稍后,我還可以對它進行一些臨時的操作,然后我就可以為它提供一個堆配置文件,并在后得到一個完全膨脹的堆表示。我有了我的數據,準備好了,準備好了我想要的任何可視化方法!
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。