在前端項目(PC端)中,內存泄露的定位往往比修復更加困難,即使google瀏覽器有提供Memory工具,但是面對成千上萬的元素和錯綜復雜的引用關系,開發則依然很難快速定位到問題代碼塊。
一、什么是內存泄漏?
系統進程不再用到的內存,沒有及時釋放,就叫做內存泄漏(memory leak)。當內存占用越來越高,輕則影響系統性能,重則導致進程崩潰。Chrome限制了瀏覽器所能使用的內存極限(64位為1.4GB,32位為1.0GB),這也就意味著瀏覽器將無法直接操作一些大內存對象。
V8引擎在執行垃圾回收時會阻塞 JavaScript應用邏輯,直到垃圾回收結束再重新執行JavaScript應用邏輯,這種行為被稱為“全停頓”(stop-the-world)。 若V8的堆內存為1.5GB,V8做一次小的垃圾回收需要50ms以上,造成假死現象。
二、JS內存管理和垃圾回收機制GC
高級語言基本都有垃圾回收機制(garbage collection)自動管理內存,降低程序員的負擔,以達到解決內存泄漏的目的,但是不允許人為手動觸發,無法對內存管理進行任何干預。
老版本的瀏覽器使用引用計數法(Reference Counting)來管理內存,即每次引用加一,被釋放時減一,當這個值的引用次數變成 0 時,就可以將其內存空間回收,缺點是循環引用時無法回收。
現代瀏覽器基本采用標記清除法(Mark-and-Sweep)來管理內存,即瀏覽器周期性地從某個根元素(譬如 window 對象)開始找引用變量,及這些變量引用的變量,這樣一直找下去。能找到的變量為可獲得變量,不能找到的將被內存回收。
缺點是清除后內存會產生很多細化的分塊,所以又衍生了標記-整理法,不細講。
三、VUE中容易出現內存泄露的幾種情況
內存泄露是一個累積的過程,只有頁面生命周期略長的時候才暴露出問題,頻繁交互能夠加快累積的過程,偏展示的頁面很難把這樣的問題暴露出來(所謂刷新一下又能滿血復活)。所以很多時候我們都是被動式的等待問題暴露然后進行排查的,主動式的分析通常比較難。vue頁面大多是單頁應用,高交互且停留時間久,處理不好很容易出現內存泄漏。本文章 主要針對游離的dom對象進行排查 ,普通的JS變量排查有時間再補充。
1.全局變量造成的內存泄露
<template> <div id="home"> 這里是首頁 </div></template><script>export default { mounted () { window.test = { // 此處在全局window對象中引用了本頁面的dom對象 name: 'home', node: document.getElementById('home') } }}</script>
按下Heap snapshots鍵,搜索Detached,發現沒有脫離文檔樹的dom元素,屬于正?,F象
改變路由跳轉到other頁面,按下Heap snapshots鍵,搜索Detached,發現有兩處dom元素游離于當前頁面之外,很明顯是window對象引用了home頁面中的div,即使此時home頁面已經銷毀,home中的dom元素卻還駐留在內存中無法釋放。
解決方案就是在頁面卸載的時候順便處理掉該引用。
<template> <div id="home"> 這里是首頁 </div></template><script>export default { mounted () { window.test = { // 此處在全局window對象中引用了本頁面的dom對象 name: 'home', node: document.getElementById('home') } }, destroyed () { window.test = null // 頁面卸載的時候解除引用 }}</script>
2.除了直接引用,window的原生方法也會起到引用dom元素使其無法釋放的效果。
<template> <div id="home">這里是首頁</div></template><script>export default { mounted () { window.addEventListener('resize', this.func) // window對象引用了home頁面的方法 }, methods: { func () { console.log('這是home頁面的函數') } }}</script>
解決方法一樣,也是在頁面銷毀的時候,順便解除引用,釋放內存
mounted () { window.addEventListener('resize', this.func)},beforeDestroy () { window.removeEventListener('resize', this.func)}
3.一些全局的方法使用不當也會造成內存無法釋放,在頁面卸載的時候也可以考慮解除引用
<template> <div id="home">這里是首頁</div></template><script>export default { mounted () { this.$EventBus.$on('homeTask', res => this.func(res)) }, methods: { func (res) { console.log(res) } }}</script>
mounted () { this.$EventBus.$on('homeTask', res => this.func(res))},destroyed () { this.$EventBus.$off()}
造成游離dom節點的原因還有很多,不止這三種,總結起來:
1.window對象、事件總線、全局vuex上綁定了已銷毀頁面上的節點,到時節點不隨頁面一起銷毀
2.使用第三方庫創建實例,第三方庫一般會提供銷毀函數,頁面跳轉時沒有調用正確的銷毀函數
3.有同學會說在頁面中使用閉包也會造成內存泄露,在vue框架里有管理內存的機制,只要按照它的正確編寫方法,理論上是不會造成內存泄露的
總結
以上所述是小編給大家介紹的vue單頁應用的內存泄露定位和修復問題小結,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!
新聞熱點
疑難解答