JavaScript 中的垃圾回收
記憶體的生命週期
javascript 的記憶體分配
javascript 垃圾回收的方法和方式
哪些操作會造成記憶體洩漏
記憶體的生命週期
1 分配所需要的記憶體
2 使用分配到的記憶體進行讀寫操作
3 不需要時將記憶體進行清除
javascript 的記憶體分配
- 變數初始化分配
var str = 'string' // 為字串分配記憶體
var arr = [1, 2] // 為陣列及數值分配記憶體
var obj = { // 為物件及承載的數值分配記憶體
a: 1
}
function fn(a, b) { // 為可呼叫的函式變數 fn 物件分配記憶體
return a + b
}
el.addEventListener('click', function() { // 函式表示式, 匿名函式分配記憶體
el.style.color = 'red'
})
- 呼叫函式分配
var d = new Date(); // 為Date 物件值分配記憶體
var e = document.createElement('div'); // 為 DOM 物件分配記憶體
javascript 垃圾回收的方法
- 引用計數
- 標記清除(常用)
引用計數
引用計數垃圾回收演算法
var o = {
a: {
b:2
}
};
// 兩個物件被建立
// { b: 2 } 作為一個屬性被引用 +1 = 1
// { a: { b: 2 } } 被分配給變數 o +1 = 1
var o2 = o; // o2變數是第二個對 { a: { b: 2 } } +1 = 2 的引用
o = 1; // 現在,{ a: { b: 2 } } 的原始引用o被o2替換了 -1 = 1
var oa = o2.a; // 引用 { a: { b: 2 } } + 1 = 2的a屬性 { b: 2 } + 1 = 2
// 現在,{ a: { b: 2 } } 有兩個引用了,一個是o2,一個是oa
o2 = "yo"; // { a: { b: 2 } } = 0 物件的原始引用被清除
// 然而它的屬性a的物件還在被oa引用,所以還不能回收
oa = null; // a屬性 { b: 2 } 現在也是零引用了
// { a: { b: 2 } } 它可以被垃圾回收了
迴圈引用
function f(){
var o = {}; + 1
var o2 = {}; + 1
o.a = o2; // o 引用 o2 + 1
o2.a = o; // o2 引用 o + 1
return "str";
}
f()
// var o = {}; var o2 = {}; 在棧中執行後該被清除
// o.a, o2.a 都至少引用了一次 o 和 o2 無法被清除
var el;
window.onload = function(){
el = document.getElementById("element");
el.circularReference = el;
el.lotsOfData = new Array(10000).join("*");
};
// 當element 元素被刪除後應該被回收
// el.circularReference 迴圈引用了 el, 導致對此 dom 元素的引用無法被回收, el.lotsOfData 的資料無法釋放
標記清除(常用)
在全域性環境或函式環境宣告變數時,進入執行環境,�垃圾回收器將其標記為'進入環境',當變數離開環境、函式執行結束後將其標記為'離開環境'。垃圾收集器會在執行時通過給儲存在記憶體中的所有變數加上標記的方式決定是否應該清除,閉包只有'進入環境'標記。垃圾收集器執行時會對標記為'離開環境'的變數和全域性環境無法訪問到的物件進行清除。
標記清除的迴圈引用
// 函式內宣告的 o 和 o2 因為在全域性環境下無法訪問會被清除
function f(){
var o = {}; + 1
var o2 = {}; + 1
o.a = o2; // o 引用 o2 + 1
o2.a = o; // o2 引用 o + 1
return "str";
}
f()
// 當 element 被刪除後或手動取消引用時,全域性環境 el 變數為null,dom 物件佔用的記憶體則被清除
var el;
window.onload = function(){
el = document.getElementById("element");
el.circularReference = el;
el.lotsOfData = new Array(10000).join("*");
};
el = null // 全域性環境無法訪問到el.circularReference 被清除
哪些操作會造成記憶體洩漏
settimeout的第一個引數使用字串而非函式的話,會引發記憶體洩漏。意外的全域性變數、閉包、控制檯日誌、遺留的定時器、在兩個物件彼此引用且彼此保留
解決方法:
函式執行後手動設定 dom 為null, 手動 clear 定時器,避免迴圈引用。
WeakMap
WeakMap 作用
WeakMap
WeakSet
對於值的引用都是不計入垃圾回收機制的,表示這是弱引用。
先新建一個 Weakmap 例項。然後,將一個 DOM 節點作為鍵名存入該例項,並將一些附加資訊作為鍵值,一起存放在 WeakMap 裡面。這時,WeakMap 裡面對element的引用就是弱引用,不會被計入垃圾回收機制。
當我們想為物件新增資料但是又不想干擾垃圾回收機制就可以使用
const wm = new WeakMap();
const element = document.getElementById('example'); // 引用計數 + 1
wm.set(element, 'some information'); // 弱引用 - 引用計數不變
wm.get(element) // "some information" value 可以為物件
WeakMap 示例
當called
大於10後 進行 report
上報 map
對 obj
引數的引用仍然存在,造成了記憶體洩漏,而我們只是為obj
新增了一些額外資訊
var map = new Map(); // maps can have object keys
function useObj(obj){
doSomethingWith(obj);
var called = map.get(obj) || 0;
called++; // called one more time
if(called > 10) report(); // 應該手動清除 map 對 obj 的引用
map.set(obj, called);
}
使用WeakMap
用於處理為物件新增資訊的場景
var map = new WeakMap(); // create a weak map
function useObj(obj){
doSomethingWith(obj);
var called = map.get(obj) || 0;
called++; // called one more time
if(called > 10) report(); // 無需清除引用
map.set(obj, called);
}
相關文章
- javascript垃圾回收JavaScript
- JavaScript的垃圾回收機制JavaScript
- javascript 垃圾回收機制JavaScript
- JavaScript中的垃圾回收和記憶體洩漏JavaScript記憶體
- Kubernetes 中的垃圾回收
- JVM 中的垃圾回收JVM
- 用垃圾回收機制解釋JavaScript中的閉包JavaScript
- javascript的垃圾回收機制指的是什麼?JavaScript
- javascript的垃圾回收機制指的是什麼JavaScript
- 垃圾回收(一)【垃圾回收的基礎】
- 垃圾回收(三)【垃圾回收通知】
- JavaScript 記憶體管理及垃圾回收JavaScript記憶體
- Flutter中的垃圾回收機制Flutter
- javascript的垃圾回收機制和記憶體管理JavaScript記憶體
- 垃圾回收
- JVM 垃圾回收演算法和垃圾回收器JVM演算法
- 聊聊Dotnet的垃圾回收
- 使用Chrome開發者工具研究JavaScript的垃圾回收機制ChromeJavaScript
- JVM垃圾回收JVM
- 垃圾回收_上
- 垃圾回收_下
- [JVM]垃圾回收JVM
- golang垃圾回收Golang
- Python:垃圾回收Python
- javascript 垃圾回收演算法瞭解一下JavaScript演算法
- PHP的垃圾回收機制-回收週期PHP
- 關於JVM的垃圾回收JVM
- JS的垃圾回收機制JS
- jvm的垃圾回收機制JVM
- PHP的垃圾回收機制PHP
- Unity GC垃圾回收UnityGC
- JVM垃圾回收概述JVM
- GC垃圾回收器GC
- JVM垃圾回收器JVM
- JVM垃圾回收(下)JVM
- 【Postgresql】VACUUM 垃圾回收SQL
- JVM - 垃圾回收概述JVM
- Javascrip高程中的垃圾記憶體回收制(6)Java記憶體