91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么排查Javascript內存泄漏

發布時間:2022-06-18 11:56:21 來源:億速云 閱讀:255 作者:iii 欄目:開發技術

這篇文章主要講解了“怎么排查Javascript內存泄漏”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“怎么排查Javascript內存泄漏”吧!

如何判斷我的應用發生了內存泄漏

為了證明螃蟹的聽覺在腿上,一個專家捉了只螃蟹并沖它大吼,螃蟹很快就跑了。然后捉回來再沖它吼,螃蟹又跑了。最后專家把螃蟹的腿都切了,又對著螃蟹大吼,螃蟹果然一動不動……

定位內存問題的過程其實也類似,如果你自己都不知道自己的頁面在使用過程中哪些步驟會導致內存增長,那很可能就會錯把一個正常的內存增長當作內存泄漏來排查,最后查了半天白忙活。 其實一個單頁應用在使用過程中,內存發生增長是很合理的。例如在開發過程中,為了優化使用體驗,我們可能會對部分數據進行緩存,這部分緩存的數據其實也會導致內存占用的升高,但它是符合預期的。因此,排查內存泄漏的第一步,就是要先梳理一遍自己的代碼,看一下哪部分內存的升高是合理的,哪部分內存的升高是不合理的。

Performance和Memory都可以用來定位內存問題,先用誰呢

答案是先用Performance。 當我們懷疑頁面發生了內存泄漏的時候,可以先用Performance錄制一段時間內頁面的性能變化。你只需要切換到Performance面板,點擊Record,然后在頁面上正常操作一段時間,最后停止錄制即可。

怎么排查Javascript內存泄漏

不斷升高的內存下限

如果錄制結束后,看到內存的下限在不斷升高的話,你就要注意了 —— 這里有可能發生了內存泄漏。

除了內存增長曲線,Nodes(Dom節點數曲線)、Document曲線以及Listener曲線也同樣值得關注,有時候它們對內存問題的定位也很有幫助。

當你懷疑發生了內存泄漏的時候,你就可以用Memory面板來進一步定位泄漏的源頭了。

通過Memory面板定位內存泄漏的流程通常是怎么樣的呢

通常,我們可以從Memory的主界面開始,點擊左上角的圓點就可以記錄下當前的堆內存快照(heap snapshot)了。

怎么排查Javascript內存泄漏

Memory面板

這里推薦一個Gmail團隊也在用的 “three snapshot”技巧:

  • 打開DevTools, 切換至Memory面板

  • 先記錄一個堆內存快照

  • 在你的頁面上執行可能發生泄漏的操作

  • 再記錄一個堆內存快照

  • 重復執行多幾遍步驟3

  • 最后記錄一個堆內存快照

  • 選擇最后一個堆內存快照,找到頂欄的“All objects”, 切換至”Objects allocated between snapshots 1 and 2”(也可以對2,3執行同樣的操作)

怎么排查Javascript內存泄漏

過濾出兩份快照之間新分配的對象

8. 切換后,你就能看到兩個快照之間新生成的對象。你可以選擇其中一項點開,看看它的retaining tree里面保留了哪些對象沒有釋放。

Tips:在記錄第一個堆快照之前你可以先做一些“預熱”操作,避免一些懶加載和緩存策略影響到了對內存的分析。

為什么我的內存快照記錄下來之后看不懂,還出現了很多奇怪的變量

這也是我排查內存泄漏時遇到的第一個問題,為什么教程里的內存快照簡潔易懂,我的內存快照卻像一本天書?

怎么排查Javascript內存泄漏

教程里的內存快照

怎么排查Javascript內存泄漏

我的內存快照

為什么有這么大的差異呢?除去教程里demo代碼比較簡單之外,提前準備好一個合理的debug環境也是很重要的。這里我列舉了4點個人覺得對debug內存問題很有幫助的措施:

1. 盡量使用沒有混淆的代碼:

打包后的代碼往往經過了混淆和壓縮,在生產環境上這是必要的,但在debug時卻會成為我們的絆腳石,不便于閱讀。

2. 排查問題時使用production模式編譯出來的代碼:

Dev模式下往往會開啟一些方便開發的特性,例如熱更新等。但它們可能會占用一部分的內存,影響到內存問題的排查,所以建議還是使用production模式編譯出來的代碼進行問題排查。

3. 屏蔽所有瀏覽器插件:

屏蔽瀏覽器插件最快的方式就是打開無痕窗口。瀏覽器插件給我們帶來很多便利,但插件注入的額外邏輯有時也會影響內存問題的排查。例如vue-devtools會記錄下每一個vuex mutaions,導致內存無法釋放。

4. 在現場打內存快照,便于跳轉到源代碼所在行:

盡管devTools記錄下來的內存快照文件可以單獨加載展示,但還是建議在記錄下內存快照的時候“趁熱”分析,因為這時還能從retaining tree上跳轉到代碼所在行,有時候對定位問題也很有幫助。

怎么排查Javascript內存泄漏

跳轉到源碼所在行

快照里有一些“Detached DOM tree”,是什么意思

一個DOM節點只有在沒有被頁面的DOM樹或者Javascript引用時,才會被垃圾回收。當一個節點處于“detached”狀態,表示它已經不在DOM樹上了,但Javascript仍舊對它有引用,所以暫時沒有被回收。通常,Detached DOM tree往往會造成內存泄漏,我們可以重點分析這部分的數據。

Shallow size 和 Retained size,它們有什么不同

Shallow size: 這是對象自身占用內存的大小。通常只有數組和字符串的shallow size比較大。

Retain size: 這是將對象本身連同其無法從 GC 根到達的相關對象一起刪除后釋放的內存大小。 因此,如果Shallow Size = Retained Size,說明基本沒怎么泄漏。而如果Retained Size > Shallow Size,就需要多加注意了。

Memory里的Summary視圖, Comparison視圖, Dominators視圖和Containment視圖分別有什么不同呢

Summary view:

顧名思義,Summary view就是當前內存快照的一個概覽。我們先介紹一下這個視圖下的每一列是什么意思: - Constructor: 對象的構造器。 - Distance:與root的距離。距離越大,處理和加載這個對象的時間就越長。 - Object Count:指定構造器創建的對象的數量。 - Shallow Size:對象自身占用內存的大小。 - Retained Size:釋放掉該對象后,能釋放掉的內存。

在這個視圖下你可以看到當前頁面內存的具體構成,但如果想定位內存問題,下面的Comparison view會更加有用。

Comparison view:

Comparison視圖可以讓你對比兩份內存快照之間的差異。默認是跟上一份快照做對比,當然你也可以選擇任意兩份內存做對比。這個視圖下每一列的數據有點不同: - Constructor: 對象的構造器。 - # New: 該對象構造器下有多少新對象被創建 - # Deleted: 該對象構造器下有多少新對象被銷毀 - # Delta: # New - # Delete的差值 - Alloc.Size:兩份快照之間新分配的內存 - Freed Size: 兩份快照之間釋放掉的內存 - Size Delta:Alloc Size - Freed Size 的差值

這個視圖絕對是排查內存泄漏的利器。當你能定位到是哪些操作可能造成內存泄漏后,比較操作前后的內存快照,很容易就能發現發生內存泄漏的對象。

Containment view:

Containment view提供了一個自下而上的視圖,它允許你瀏覽和探索堆內存的內容。我們可以用它來分析一些全部變量的引用情況(如window)。

Statistics view:

Statistics視圖會用餅圖的形式展示各個類型對象的內存占比

Constructor下的(array), Array, (closure), (compiled code)都對應的哪些內容?

  • (closure): 函數閉包持有的內存引用。

  • (array, string, number, regex): 包含著一系列對象,這些對象的屬性上有對應類型變量的引用。

  • (compiled code): Javascript引擎(如V8)為了加快運行速度,會對代碼進行一次編譯。(compiled code)顧名思義就是指與編譯后的代碼相關聯的內存。

  • Detached HTMLDivElement等:代碼里對指定類型Dom節點的引用。

發現有一個叫feedback_cell的字段經常出現,它是什么?是它導致了內存泄漏嗎?

怎么排查Javascript內存泄漏

經常出現的feedback_cell

放心,它不會造成內存泄漏。它是v8對頻繁運行的熱代碼做出的優化,會被v8自己回收。詳見這篇文章:Feedback vectors in heap snapshots – Rohit Pagariya

常見的內存泄漏場景有哪些?

這里列舉了一些常見的內存泄漏場景,遇到內存泄漏問題時可以先自查一遍常見場景,個人感覺能解決日常開發中遇到的90%內存泄漏

  • console導致的內存泄漏 因為打印后的對象需要支持在控制臺上查看,所以傳遞給console.log方法的對象是不能被垃圾回收的。我們需要避免在生產環境用console打印對象。

  • 框架配合第三方庫使用時,沒有及時執行銷毀 這點可以參考vue cookbook里的例子

  • 被遺忘的定時器 例如在組件初始化的時候設置了setInterval,那么在組件銷毀之前記得調用clearInterval方法取消定時器。

  • 沒有正確移除事件監聽器(各種EventBus, dom事件監聽等) 這應該是最容易犯的一個錯誤,無論新手老手都有可能栽在這里。
    特征:performance里,監聽器數量會持續上升

怎么排查Javascript內存泄漏

持續上升的監聽器數量

啰嗦一句:盡管大部分同學都會有主動移除監聽器的觀念,但如果姿勢不對,可能依舊會造成內存泄漏。下面是一個真實案例:

// 版本一
mounted() {
    window.addEventListener('resize', debounce(this.handleWidthChange, 100))
},
beforeDestroy() {
    window.removeEventListener('resize', debounce(this.handleWidthChange, 100)) 
}

乍一看好像寫的還不錯,有及時移除監聽器,對resize這種頻繁觸發的事件也加了debounce處理。但其實這段代碼就導致了內存泄漏:每次調用debounce(this.handleWidthChange, 100)時, 其實都會返回一個新的函數,導致addEventListener和 removeEventListener方法傳入的回調函數已經不是同一個回調函數,監聽器沒有被正確移除,內存泄漏。

下面來看修改后的代碼:

// 版本二
data() {
    return {
        debounceWidthChange: null
    }
},
mounted() {
    this.debounceWidthChange = debounce(this.handleWidthChange, 100)
    window.addEventListener('resize', this.debounceWidthChange)
},
beforeDestroyed() {
    window.removeEventListener('resize', this.debounceWidthChange)  
}

修改后,監聽和移除監聽的已經是同一個回調函數了,看起來似乎已經沒問題。然而,這段代碼還是有內存泄漏的問題。沒看出問題的小伙伴可以對比一下正確答案:

// 版本三
data() {
    return {
        debounceWidthChange: null
    }
},
mounted() {
    this.debounceWidthChange = debounce(this.handleWidthChange, 100)
    window.addEventListener('resize', this.debounceWidthChange)
},
beforeDestroy() {
    window.removeEventListener('resize', this.debounceWidthChange)  
}

是的,答案非常狗血:Vue只有destroyedbeforeDestroy這兩個生命周期,沒有 beforeDestroyed,所以上面的beforeDestroyed函數永遠不會執行,導致了內存泄漏。

感謝各位的閱讀,以上就是“怎么排查Javascript內存泄漏”的內容了,經過本文的學習后,相信大家對怎么排查Javascript內存泄漏這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

浦北县| 招远市| 新干县| 界首市| 荣成市| 蚌埠市| 井陉县| 塔城市| 天柱县| 安庆市| 青铜峡市| 禄丰县| 嵊州市| 中西区| 余姚市| 天柱县| 武清区| 洪湖市| 绥江县| 山东| 南召县| 彭水| 长沙县| 昌乐县| 布尔津县| 勃利县| 达拉特旗| 安福县| 青冈县| 平湖市| 游戏| 白河县| 南岸区| 北宁市| 繁峙县| 灵武市| 孝感市| 呼伦贝尔市| 永嘉县| 温宿县| 涟水县|