Js中常見的內存泄漏場景及處理方式


常見的內存泄漏場景

全局變量的意外創建

一個未聲明變量的引用會在全局對象中創建一個新的變量。

沒有使用var來創建變量

function foo(arg) {
    bar = "this is a hidden global variable";
}
//等價於
function foo(arg) {
    window.bar = "this is an explicit global variable";
}

對於this的錯誤使用

function foo() {
    this.variable = "potential accidental global";
} 
foo();

foo函數再全局作用域中被調用,因此this指向window

解決方式: 在js文件開頭添加 ‘use strict’,開啟嚴格模式。(或者一般將使用過后的全局變量設置為 null 或者將它重新賦值,這個會涉及的緩存的問題,需要注意)

閉包引起的內存泄漏

閉包是JavaScript開發的一個關鍵方面,閉包可以讓你從內部函數訪問外部函數作用域,簡單來說可以認為是可以從一個函數作用域訪問另一個函數作用域而非必要在函數作用域中實現作用域鏈結構。由於閉包會攜帶包含它的函數的作用域,因此會比其他函數占用更多的內存,過度使用閉包可能會導致內存占用過多,在不再需要的閉包使用結束后需要手動將其清除。
例如

function debounce(wait, funct, ...args){ // 防抖函數
    var timer = null;
    return () => {
        clearTimeout(timer);
        timer = setTimeout(() => funct(...args), wait);
    }
}

window.onscroll = debounce(300, (a) => console.log(a), 1);

被遺忘的計時器

計時器setInterval必須及時清理,否則可能由於其中引用的變量或者函數都被認為是需要的而不會進行回收,如果內部引用的變量存儲了大量數據,可能會引起頁面占用內存過高,這樣就造成意外的內存泄漏。
解決方式:當不需要interval或者timeout的時候,調用clearInterval或者clearTimeout

DOM之外的引用

有時保存DOM節點內部數據結構很有用,例如需要快速更新表格的幾行內容,把每一行DOM存成字典或者數組很有意義。此時同樣的DOM元素存在兩個引用:一個在DOM樹中,另一個在字典中。將來如果決定刪除這些行時,需要把兩個引用都清除。此外還要考慮DOM樹內部或子節點的引用問題,假如你的JavaScript代碼中保存了表格某一個<td>的引用,將來決定刪除整個表格的時候,直覺認為GC會回收除了已保存的<td>以外的其它節點,實際情況並非如此,此<td>是表格的子節點,子元素與父元素是引用關系,由於代碼保留了<td>的引用,導致整個表格仍待在內存中,所以在保存DOM元素引用的時候,要小心謹慎。

var elements = {
    button: document.getElementById("button"),
    image: document.getElementById("image"),
    text: document.getElementById("text")
};
function doStuff() {
    elements.image.src = "https://www.example.com/1.jpg";
    elements.button.click();
    console.log(elements.text.innerHTML);
    // 更多邏輯
}
function removeButton() {
    // 按鈕是 body 的后代元素
    document.body.removeChild(elements.button);
    elements.button = null; // 清除對於這個對象的引用
}

事件監聽器被遺忘

當事件監聽器在組件內掛載相關的事件處理函數,而在組件銷毀時不主動將其清除時,其中引用的變量或者函數都被認為是需要的而不會進行回收,如果內部引用的變量存儲了大量數據,可能會引起頁面占用內存過高,這樣就造成意外的內存泄漏。

MAP被遺忘

當使用Map存儲對象時,類似於脫離DOM的引用,如果不將其主動清除引用,其同樣會造成內存不自動進行回收,對於鍵為對象的情況,可以采用WeakMapWeakMap對象同樣用來保存鍵值對,對於鍵是弱引用的而且必須為一個對象,而值可以是任意的對象或者原始值,且由於是對於對象的弱引用,其不會干擾Js的垃圾回收。

SET被遺忘

當使用Set存儲對象時,類似於脫離DOM的引用,如果不將其主動清除引用,其同樣會造成內存不自動進行回收,如果需要使用Set引用對象,可以采用WeakSetWeakSet對象允許存儲對象弱引用的唯一值,WeakSet對象中的值同樣不會重復,且只能保存對象的弱引用,且由於是對於對象的弱引用,其不會干擾Js的垃圾回收。

console的濫用

控制台日志記錄對總體內存內置文件的影響,也是個重大的問題,同時也是容易被忽略的。記錄錯誤的對象,可以將大量的數據保留在內存中。傳遞給console.log的對象是不能被垃圾回收,所以沒有去掉console.log可能會存在內存泄漏

被遺忘的監聽者模式

當實現了監聽者模式並在組件內掛載相關的事件處理函數,而在組件銷毀時不主動將其清除時,其中引用的變量或者函數都被認為是需要的而不會進行回收,如果內部引用的變量存儲了大量數據,可能會引起頁面占用內存過高,這樣就造成意外的內存泄漏。

<template>
  <div ></div>
</template>

<script>
export default {
  created: function() {
    global.eventBus.on("test", this.doSomething);
  },
  beforeDestroy: function(){
      global.eventBus.off("test", this.doSomething);
  },
  methods: {
    doSomething: function() {
        // do something
    },
  },
}
</script>

強引用可能會導致內存泄漏

參考

https://www.cnblogs.com/WindrunnerMax/p/13944643.html
https://www.cnblogs.com/endlessmy/p/8688056.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM