前言javascript
Profiles面板功能的做用主要是監控網頁中各類方法執行時間和內存的變化,簡單來講它就是Timeline的數字化版本。它的功能選項卡不是不少(只有三個),操做起來比較前面的幾塊功能版原本說簡單,可是裏面的數據確不少,很雜,要弄懂它們須要花費一些時間。尤爲是在內存快照中的各類龐雜的數據。在這篇博客中滷煮將繼續給你們分享Chrome開發者工具的使用經驗。若是你遇到不懂的地方或者有不對的地方,能夠在評論中回覆滷煮,文章最後滷煮會最後把祕籍交出來。下面要介紹的是Profiles。首先打開Profiles面板。html
Profiles界面分爲左右兩個區域,左邊區域是放文件的區域,右邊是展現數據的區域。在開始檢測以前能夠看到右邊區域有三個選項,它們分別表明者不一樣的功能:
1.(Collect JavaScript CPU Profile)監控函數執行期花費的時間
2.(Take Heap Snapshot)爲當前界面拍一個內存快照
3.(Record Heap Allocations)實時監控記錄內存變化(對象分配跟蹤)java
1、Collect JavaScript CPU Profile(函數收集器)正則表達式
首先來關注第一個功能,(Collect JavaScript CPU Profile)監控函數執行期花費的時間。講道理不如舉例子,爲了更清楚地瞭解它的功能概況,咱們能夠編寫一個測試列子來觀察它們的做用。這個列子簡單一些,使得咱們分析的數據更清晰一些。chrome
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<button id="btn"> click me</button>
<script type="text/javascript">
function a() { console.log('hello world'); } function b() { a(); } function c() { b(); } document.getElementById('btn').addEventListener('click', c, true); </script>
</body>
</html>
在右邊區域中選擇Collect JavaScript CPU Profile 選項,點擊下方的Start按鈕(也能夠點擊左邊的黑色圓圈),這時候Chrome會開始記錄網頁的方法執行,而後咱們點擊界面的按鈕來執行函數。最後再點擊右邊區域的Stop按鈕(或者左邊的紅色圓圈),這時監控就結束了。左邊Profiles會列出一個文件,單擊能夠看到以下界面:
生存了一個數據表格,它們的意義在上圖中已經標記出來了。它記錄的是函數執行的時間以及函數執行的順序。經過右邊區域的類型選項能夠切換數據顯示的方式。有正包含關係,逆包含關係,圖表類型三種選項。咱們能夠選擇其中的圖表類型:數組
能夠看到這個面板似曾相識,沒錯,它跟以前的TimeLine面板很像,的確,雖然很像,但功能不同,否則也就不必重複作了。從上圖能夠看到點擊按鈕執行的各個函數執行的時間,順序,包含關係和CUP變化等。你能夠在生成文件以後在左邊區域中保存該文件記錄,下次只須要在區域2這中點擊load按鈕即可以加載出來。也就是說你能夠本地永久地記錄該段時間內的方法執行時間。第一個功能大概就這麼多,比較其餘兩個來講簡單。瀏覽器
2、Take Heap Snapshot(內存快照)緩存
下面咱們來介紹一下第二個功能的用法。第二個功能是給當前網頁拍一個內存快照.選擇第二個拍照功能,按下 Take Snapshot 按鈕,給當前的網頁拍下一個內存快照,獲得以下圖。
能夠看到左邊區域生成個文件,文件名下方有數字,表示這個張快照記錄到的內存大小(此時爲3.2M)。右邊區域是個列表,它分爲五列,表頭能夠按照數值大小手動排序。在這張表格中列出的一些列數字和標識,以及表頭的意義比較複雜,涉及到一些js和內存的知識,咱們就先從這些表頭開始瞭解他們。從左到右的順序它們分別表示:
Constructor(構造函數)表示全部經過該構造函數生成的對象
Distance 對象到達GC根的最短距離
Objects Count 對象的實例數
Shallow size 對應構造函數生成的對象的shallow sizes(直接佔用內存)總數
Retained size 展現了對應對象所佔用的最大內存
CG根!是神馬東西?在google的官方文檔中的建議是CG根沒必要用到開發者去關心。可是咱們在這裏能夠簡單說明一下。你們都知道js對象能夠互相引用,在某個對象申請了一塊內存後,它極可能會被其餘對象應用,而其餘對象又被另外的對象應用,一層一層,但它們的指針都是指向同一塊內存的,咱們把這最初引用的那塊內存就能夠成爲GC根。用代碼表示是這樣的:閉包
var obj = {a:1}; obj.pro = { a : 100 }; obj.pro.pro = { b : 200 }; var two = obj.pro.pro; //這種狀況下 {b:200} 就是被two引用到了,{b:200}對象引用的內存就是CG根
用一張官方的圖能夠以下表示:dom
構成這張關係網的元素有兩種:
Nodes:節點,對應一個對象,用建立該對象的構造方法來命名
Edges:鏈接線,對應着對象間的引用關係,用對象屬性名來命名
從上圖你也能夠看到了第二列的表頭Dishtance的意義是什麼,沒錯,它指的就是CG根和引用對象之間的距離。根據這條解釋,圖中的對象5到CG根的距離就是2!那麼什麼是直接佔用內存(Shallow size)和最大佔用內存(Retained size)呢?直接佔用內存指的是對象自己佔用的內存,由於對象在內存中會經過兩種方式存在着,一種是被一個別的對象保留(咱們能夠說這個對象依賴別的對象)或者被Dom對象這樣的原生對象隱含保留。在這裏直接佔有內存指的就是前一種。(一般來說,數組和字符串會保留更多的直接佔有內存)。而最大內存(Retained size)就是該對象依賴的其餘對象所佔用的內存。你要明白這些都是官方的解釋,因此即便你以爲雲裏霧裏也是正常的,官方解釋確定是官腔嘛。按照滷煮本身的理解是這樣的:
function a() { var obj = [1,2,.......n]; return function() { //js做用域的緣由,在此閉包運行的上下文中能夠訪問到obj這個對象 console.log(obj); } } //正常狀況下,a函數執行完畢 obj佔用的內存會被回收,可是此處a函數返回了一個函數表達式(見Tom大叔的博客函數表達式和函數聲明),其中obj由於js的做用域的特殊性一直存在,因此咱們能夠說b引用了obj。 var b = a(); //每次執行b函數的時候均可以訪問到obj,說明內存未被回收 因此對於obj來講直接佔用內存[1,2,....n], 而b依賴obj,所obj是b的最大內存。 b()
在dom中也存在着引用關係:咱們經過代碼來看下這種引用關係:
<html> <body> <div id="refA"> <ul> <li><a></a></li> <li><a></a></li> <li><a id="#refB"></a></li> </ul> </div> <div></div> <div></div> </body> </html> <script> var refA = document.getElementById('refA'); var refB = document.getElementById('refB');//refB引用了refA。它們之間是dom樹父節點和子節點的關係。 </script>
如今,問題來了,若是我如今在dom中移除div#refA會怎麼樣呢?答案是dom內存依然存在,由於它被js引用。那麼我把refA變量置爲null呢?答案是內存依然存在了。由於refB對refA存在引用,因此除非在把refB釋放,不然dom節點內存會一直存在瀏覽器中沒法被回收掉。上圖:
因此你看到Constructor這一列中對象若是有紅色背景就表示有可能被JavaScript引用到可是沒有被回收。以上只是滷煮我的理解,若是不對頭,請你必定要提醒滷煮好即時更新,省得誤人子弟!接着上文,Objects Count這一列是什麼意思呢?Objects Count這一列的意義比較好理解,從字面上咱們就知道了其意義。就是對象實例化的數量。用代碼表示就是這樣的:
var ConstructorFunction = function() {};//構造函數
var a = new ConstructorFunction();//第一個實例
var b = new ConstructorFunction();//第二個實例
....... var n = new ConstructorFunction();//第n個實例
能夠看到構造函數在上面有n個實例,那麼對應在Objects Count這列裏面就會有數字n。在這裏,ConstructorFunction是咱們本身定義的構造函數。那麼這些構造函數在哪裏呢,聰明的你必定能夠猜到就在第一列Constructor中。實際上你能夠看到列表中的Constructor這一列,其中多數都是系統級別的構造函數,有一部分也是咱們本身編寫的:
global property - 全局對象(像 ‘window’)和引用它的對象之間的中間對象。若是一個對象由構造函數Person生成並被全局對象引用,那麼引用路徑就是這樣的:[global] > (global property > Person。這跟通常的直接引用彼此的對象不同。咱們用中間對象是有性能方面的緣由,全局對象改變會很頻繁,非全局變量的屬性訪問優化對全局變量來講並不適用。
roots - constructor中roots的內容引用它所選中的對象。它們也能夠是由引擎自主建立的一些引用。這個引擎有用於引用對象的緩存,可是這些引用不會阻止引用對象被回收,因此它們不是真正的強引用(FIXME)。
closure - 一些函數閉包中的一組對象的引用
array, string, number, regexp - 一組屬性引用了Array,String,Number或正則表達式的對象類型
compiled code - 簡單來講,全部東西都與compoled code有關。Script像一個函數,但其實對應了<script>的內容。SharedFunctionInfos (SFI)是函數和compiled code之間的對象。函數一般有內容,而SFIS沒有(FIXME)。
HTMLDivElement, HTMLAnchorElement, DocumentFragment 等 – 你代碼中對elements或document對象的引用。
點擊展開它們查看詳細項,@符號表示該對象ID。:
一個快照能夠有多個試圖,在左邊區域的右上角咱們能夠看到點擊下拉菜單能夠獲得四個個任務視圖選項:
他們分別表明:
Summary(概要) - 經過構造函數名分類顯示對象;
Comparison(對照) - 顯示兩個快照間對象的差別;
Containment(控制) - 探測堆內容;
Statistic(圖形表)-用圖表的方式瀏覽內存使用概要Comparison是指對比快照之間的差別,你能夠首先拍一個快照A,操做網頁一段時間後拍下另一個快照B,而後在B快照的右邊距區域的左上角選擇該選項。而後就能夠看到對比圖。上面顯示的是每一個列,每一項的變化。在對照視圖下,兩個快照之間的不一樣就會展示出來了。當展開一個總類目後,增長和刪除了的對象就顯示出來了:
嘗試一下官方示例幫助你瞭解對比的功能。
你也能夠嘗試着查看Statistic選項,它會以圖表的方式描述內存概況。
3、Record Heap Allocations.(對象跟蹤器)
好了,第二個功能也介紹完了,最後讓咱們來瞧瞧最後一個功能Record Heap Allocations.這個功能是幹啥的呢。它的做用是爲爲咱們拍下一系列的快照(頻率爲50ms),爲咱們檢測在啓用它的時候每一個對象的生存狀況。形象一點說就是假如拍攝內存快照的功能是照相那麼它功能至關於錄像。當咱們啓用start按鈕的時候它便開始錄像,直到結束。你會看到左側區域上半部分有一些藍色和灰色的柱條。灰色的表示你監控這段時間內活躍過的對象,可是被回收掉了。藍色的表示依舊沒有沒回收。你依舊能夠滑動滾輪縮放時間軸。
對象跟蹤器功能的好處在於你能夠接二連三的跟蹤對象,在結束時,你能夠選擇某個時間段內(好比說藍色條沒有變灰)查看期間活躍的對象。幫助你定位內存泄露問題。
4、結束
好了,差很少把Profiles講完了。這東西對咱們查找內存泄露來講仍是蠻有做用的。對於工具來講,主要是多用,熟能生巧嘛。若是你以爲不過癮,我推薦你去閱讀官方文檔,裏面有N多的例子,N多的說明,很是詳細。前提是你能跳到牆外去。固然也有翻譯文檔(滷煮的祕籍都給你了,推薦一下吧)。最後真的是要像一片文章裏面寫的同樣「感謝發明計算機的人,讓咱們這些剪刀加漿糊的學術土匪變成了複製加粘貼版的學術海盜。」下期是Console和Audits。敬請關注。