中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

JS前端的內存處理的方法是什么

發布時間:2023-04-11 15:07:46 來源:億速云 閱讀:130 作者:iii 欄目:開發技術

這篇文章主要介紹了JS前端的內存處理的方法是什么的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇JS前端的內存處理的方法是什么文章都會有所收獲,下面我們一起來看看吧。

一、內存的儲存和代碼執行的場所關系

對于任何語言來說,內存管理、垃圾回收等知識都是進階路上繞不開的坎。出來面試估計也沒少被問到“前端的內存處理你了解么? 你知道js中的垃圾處理機制嗎? 什么情況會導致內存泄漏呢?

1. 儲存空間

兩種結構:

  • 棧空間:

  • 存儲原始類型

  • 執行上下文 (代碼空間:主要存儲可執行代碼)

  • 堆空間:存儲引用類型

為什么不都用棧存呢?

因為需要用棧來維護程序執行期間上下文的狀態,如果棧空間大了話,所有的數據都存放在棧空間里面,那么會影響到上下文切換的效率,進而又影響到整個程序的執行效率。

2. 內存的生命周期

內存分配:聲明變量、函數、對象的時候 內存的使用:讀寫內存,使用變量 函數等 內存回收:使用完畢,由垃圾回收機制自動回收不再使用的內存

3. js 中的內存分配和使用

// 分配
const num = 123;  // 分配到棧
const str = 'sss';// 分配到棧
const obj = {};   // 分配到堆
// 使用
const a = 10;
console.log(a); // 使用

4. 調用棧下移ESP(記錄當前執行狀態的指針)

當一個函數執行結束之后,JavaScript 引擎會通過向下移動 ESP 來銷毀該函數保存在棧中的執行上下文。 比方下面這個例子通過ESP 狀態來展示就如圖所示。

var foo1 = () => {
    console.log('foo1')
    foo2()
}
var foo2 = () => {
    console.log('foo2')
}
foo1()

JS前端的內存處理的方法是什么

被ESP指針移開后的函數作用域foo1 明顯屬于不在被引用,后續將會直接被GC回收

二、 js中的垃圾回收機制

有C語言經驗的開發者,一定明白內存聲明分配好之后,需要手動 free 的操作,這就是手動回收。而 Js 本身是自動回收機制,所以開發者不需要過多關注內存分配和釋放的問題。這些工作都讓 V8 引擎中的垃圾回收器(GC)給承包了。

最早接觸 js 的時候,市面上對于 js 內存管理、垃圾回收主要講的是下面兩種概念:

1. 引用計數法

引用計數法的算法主要依賴于引用的概念,這個回收機制最早是在 IE 在使用的。目前主流瀏覽器都使用標記清除法了。看一個對象是否有指向他的引用,如果沒有其他對象指向他了,說明當前這個對象不再被需要了。

他的缺陷在于:循環引用

如果兩個對象相互引用,盡管他們已不再被使用,但是引用計數無法識別,導致內存泄漏。

2. 標記清除法(Mark-Sweep)

將“不再使用的的對象”定義為“無法到達的對象

從根部js的全局對象觸發,定時遞歸掃描內存中的對象,凡是無法從根部到達的對象,就會被標記為不再使用,稍后進行回收。

執行過程如下:

  • GC在運行的時候會給內存中的所有變量都加上標記

  • 將從根部觸發能夠觸及到的對象標記清除

  • 剩下的還有標記的變量被視為準備刪除的變量

  • GC銷毀帶有標記的值 回收內存空間

三、代際假說和分代收集

代際假說(The Generational Hypothesis)是現代瀏覽器垃圾回收策略的基礎。整個模型可以看看下圖

JS前端的內存處理的方法是什么

新生代(副垃圾回收器)

存放的是生存時間短、占用空間較小的的對象,通過 Scavenge 算法,是把新生代空間對半劃分為兩個區域,一半是對象區域,一半是空閑區域。新的對象都要放到對象區,當快滿的時候,將還存活的對象復制到空閑區后進行角色互換。并且執行對象晉升策略,對象區域和空閑區域翻轉兩次還存在的對象,升級到老生代。這個復制翻轉的過程也避免內存碎片的產生。

老生代(主垃圾回收器)

存放的生存時間久的對象或者大的對象,使用標記清除的算法進行垃圾回收。一旦執行垃圾回收算法,都需要將正在執行的 JavaScript 腳本暫停下來,待垃圾回收完畢后再恢復腳本執行。這種行為稱為全停頓(Stop-The-World)。實際上瀏覽器為了避免垃圾回收卡頓通過增量標記方式將回收任務拆解成多個小任務穿插在js主線程中執行。

四、常見的內存泄漏

1. 全局變量

function foo() {
    bar1 = 'aaa'; // 相當于聲明在window.bar1
    this.bar2 = 'aaaa'
}
foo(); // 執行函數事this指向window ,相當于一個函數給全局變量增加了兩個變量

2. 未被清理的定時器和回調函數

//setInterval
//setTimeout
setInterval(() => {
    console.log('test')
}, 500)
//沒用用 clearInterval clearTimeout 做清除

3. 閉包

個人最喜歡《你不知道的js》里對閉包的描述

一個內部函數,有權訪問包含其的外部函數的變量 —— 《你不知道的js》 或者也可以用“內存逃逸”這種高逼格的屬于形容。

// 閉包 gc 案例
var one = null;
var replace = function() {
    var originalOne = one;
    var unused =function() {
        if(originalOne) {
            console.log(111);
        }
    }
    one = {
        longString: '111',
        method: function() {
            console.log()
        }
    }    
}
setInterval(replace, 500)

每次調用 replace, one 得到一個包含字符串和一個對于新閉包 method 的對象 unused 引用了 originOne

5. DOM 引用

var elements= {
    image: document.getElementById('111');
}
elements.image = null;

6. 怎么避免呢?

  • 盡量減少全局變量

//盡可能少寫
window.object = {} // 這類代碼
  • 使用完引用數據后,及時解除引用.null

let obj = {}
...
obj = null;
  • 避免死循環等持續執行的操作(例如 邊界判斷不清晰的 for 或 while 循環)

  • 多使用 WeakSet / WeakMap 特性

// Vue3 中就大量用了WeakMap 優化實例引用
const bucket = new WeakMap();
...
const obj = new Proxy(data, {
  get(target, key) {
    track(target, key);
    return target[key];
  },
  set(target, key, newVal) {
    target[key] = newVal;
    trigger(target, key);
  },
});
function track(target, key) {
  if (!activeEffect) return;
  let depsMap = bucket.get(target);
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()));
  }
  // 再根據key 從 depsMap 中取得 deps, 它是一個 Set 類型,里面存儲著左右與當前 key 相關聯的副作用函數: effects
  let deps = depsMap.get(key);
  // 如果 deps 不存在,同樣新建一個 Set 并與 key 關聯
  if (!deps) {
    depsMap.set(key, (deps = new Set()));
  }
  deps.add(activeEffect);
  activeEffect.deps.push(deps);
}
...

關于“JS前端的內存處理的方法是什么”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“JS前端的內存處理的方法是什么”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

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

js
AI

衡阳县| 台前县| 濮阳市| 商城县| 金川县| 正安县| 新泰市| 准格尔旗| 枝江市| 密云县| 蓬溪县| 连云港市| 福清市| 大新县| 阿荣旗| 沂水县| 神木县| 翼城县| 通榆县| 青川县| 施甸县| 运城市| 龙江县| 安义县| 敖汉旗| 宣化县| 汉中市| 博兴县| 中阳县| 兴文县| 贵州省| 金华市| 固阳县| 溆浦县| 壶关县| 通海县| 阜平县| 汉寿县| 日喀则市| 福州市| 汕头市|