1. 程式人生 > >WeakMap 本身釋放,而 keyObject 沒有釋放的情況下,value 會釋放嗎?

WeakMap 本身釋放,而 keyObject 沒有釋放的情況下,value 會釋放嗎?

部落格園markdown不太好看,可以轉到git閱讀https://sologgfun.github.io/look/

const keyObject = ['keyObject'];
new WeakMap().set(keyObject, ['value']);

問題:現在 ['value'] 會被釋放嗎?

聽說WeakMap是o(1)複雜度的,而且不會存在記憶體洩漏問題,那麼就只有一種實現機制,就是value直接通過一個隱形鍵掛在keyObject上。

但如果是這樣,而WeakMap本身又沒有引用它之前新增過那些內容,那麼是不是如果keyObject不釋放,即便WeakMap例項釋放了,通過該WeakMap例項新增在keyObject上的value是不是也都不會釋放,從而形成另一種記憶體洩漏?

jsperf.com只能測試效能,不知道記憶體洩漏該如何測試?


答案:會正確被釋放

測試過程:

[1]Chrome DevTools 控制檯上有一個小眾的 API 叫

queryObjects()

,它可以從原型樹上反查所有直接或間接的繼承了某個物件的其它物件,比如

queryObjects(Array.prototype)

可以拿到所有的陣列物件,

queryObjects(Object.prototype)

則基本上可以拿到頁面裡的所有物件了(除了繼承自Object.create(null)的物件之外)。而且關鍵是這個 API 會在記憶體裡搜尋物件前先進行一次垃圾回收。

【測試1】

const key = new WeakMap();
const map = new WeakMap();
map.set(key, new WeakMap());
undefined;

在chrome控制檯執行

查到了 3 個物件,符合預期

【測試2】

const key = new WeakMap();
new WeakMap().set(key, new WeakMap());
undefined;

在chrome控制檯執行

只有一個WeakMap沒有被回收


那麼WeakMap是怎麼做到的呢?

核心在於WeakMap上的kv對是弱引用的

V8 的實現,是在 GC 上開洞的
https://github.com/v8/v8/blob/4b9b23521e6fd42373ebbcb20ebe03bf445494f9/src/objects/hash-table.h#L336

https://github.com/v8/v8/blob/4b9b23521e6fd42373ebbcb20ebe03bf445494f9/src/heap/scavenger.cc#L465WeakMap

裡用的就是這個 EphemeronHashTable,EphemeronHashTable 儲存著的鍵和值都是弱引用

let wm = new WeakMap([[k1, v1], [k2, v2]]) // vm = {k1:v1, k2:v2}

wm.size // no such property

wm.keys(); // no such function

wm.forEach(...) // unable to be iterated

WeakMap有2個特點

  1. 屬性不可列舉
  2. key必須是Object型別

看一下WeakMap的polyfill

var WeakMap = function() {
    this.name = '__wm__' + uuid()
};

WeakMap.prototype = {
    set: function(key, value) {
        Object.defineProperty(key, this.name, {
            value: [key, value],
        });
        return this;
    },
    get: function(key) {
        var entry = key[this.name];
        return entry && (entry[0] === key ? entry[1] : undefined);
    },
    ...
};
  1. weakmap.set(key, val)事實上是直接通過Object.defineProperty給這個key加了一個新屬性

—— WeakMap的key必須是Object型別的原因

  1. 相比Map,WeakMap持有的只是每個鍵值對的“弱引用”,不會額外開記憶體儲存鍵值引用。這意味著在沒有其他引用存在時,垃圾回收器能正確處理key指向的記憶體塊。

—— WeakMap的key不可列舉的原因


延伸閱讀

1.Object.defineProperty(obj, "prop", propDesc)和obj.prop = value的區別?

[譯]JavaScript中的屬性:定義和賦值的區別

2.什麼是弱引用?

垃圾回收機制不考慮對該物件的引用。

也就是說,如果其他物件都不再引用該物件,那麼垃圾回收機制會自動回收該物件所佔用的記憶體,不考慮該物件是否還在該弱引用的結構中。

WeakMap不能遍歷,是因為成員都是弱引用,隨時可能消失,遍歷機制無法保證成員的存在,很可能剛剛遍歷結束,成員就取不到了。


參考資料

  1. https://www.zhihu.com/question/344771857
  2. https://www.jianshu.com/p/8c4ffa77b346
  3. http://es6.ruanyifeng.com/#docs/set-map#WeakSet