目錄


弱引用(weak reference) & 垃圾回收

弱引用相對於強引用,它們都和垃圾回收機制有關目前除了C、C++、Rust以外多數語言有垃圾回收機制

當new出一個實例後,系統會進行記憶體分配,並回傳一個位置(address),這個位置就是指標(pointer)

沒有垃圾回收的語言需要自己進行記憶體管理,否則會記憶體溢出

有垃圾回收的語言,當物件不會再被用到後會由垃圾收集行程(Garbage Collection)幫忙釋放記憶體,開發者不用自行管理記憶體。效能會比沒有的語言差

比喻的話,前者是沒有清潔工,要自己去丟垃圾大樓,後者是有清潔工,清潔工會定期巡視,並幫你把垃圾拿去丟掉的大樓

後者效能較差原因主要有2個

  1. 清潔工要先巡視有沒有像是垃圾的東西(跑垃圾收集行程也會花費CPU資源)
  2. 清潔工還要判斷東西是不是垃圾(使用Reference counting需要額外空間進行標記)。至於前者是自己拿去丟,總不會不知道哪些是垃圾吧


WeakMap

WeakMap是由key和value組成的map,其key必須是物件。WeakMap的key可以垃圾回收的,且若key被回收,其value也會跟著被回收

WeakMap不可迭代,故沒有.entries()、.values()、keys()、.forEach()之類的方法

也不能監測其key是否存活,故key不能列舉的(not enumerable)。如果WeakMap物件要有一個方法可以列舉key的話,那列出的key因為受垃圾回收影響,每次得到的結果可能不同

WeakMap allows associating data to objects in a way that doesn't prevent the key objects from being collected, even if the values reference the keys. However, a WeakMap doesn't allow observing the liveness of its keys, which is why it doesn't allow enumeration; if a WeakMap exposed any method to obtain a list of its keys, the list would depend on the state of garbage collection, introducing non-determinism.

如果希望能列出key的話應該用Map


WeakMap vs Map

前一段提到Map的key可列舉,故用key找Map中對應的value時有2個缺點

  1. 取值和存值都要迭代key,時間複雜度為O(n)
  2. Map會有1個裝了key、1個裝了value的陣列,因為這2個陣列,所以Map的key才不會被回收,但因為這2個陣列,所以會有記憶體流失(memory leak)的問題

    In computer science, a memory leak is a type of resource leak that occurs when a computer program incorrectly manages memory allocations in a way that memory which is no longer needed is not released.

相反地WeakMap因為key會被垃圾回收,所以則沒有以上問題


儲存DOM Element

WeakMap其中一個常見的用法是監測DOM元素

因為WeakMap會被垃圾回收,所以DOM元素被移除後WeakMap內對應的key就會被回收,故多占記憶體

<body>
  <button class="btn1">btn1</button>
  <button class="btn2">btn2</button>

  <script>
    const btn = document.querySelector('.bnt1');
    const btn2 = document.querySelector('.bnt2');

    const wm = new WeakMap();
    wm.set(btn, {clickCnt: 0});
    wm.set(btn2, {clickCnt: 0});

    btn.addEventListener('click', function() {
        let btnData = wm.get(btn);
        btnData.clickCnt++;
        console.log(btnData);

    }, false);

    btn2.addEventListener('click', function() {
        let btnData = wm.get(btn2);
        btnData.clickCnt++;
        console.log(btnData);

    }, false);    

    console.log(wm); 
    // 點3下後的結果
    // WeakMap {button.bnt1 => {…}}
    // [[Entries]]
    // 0: {button.bnt1 => Object}
    // key: button.bnt1
    // value: {clickCnt: 3}  

    // 移除DOM node後WeakMap key將會被回收
    btn.remove();

    console.log(wm); // ReferenceError: vm is not defined
  </script>
</body>

另外Vue3的響應式系統就是用WeakMap當作緩存,以避免重復代理物件


參考資料

Strong vs Weak References

MDN - WeakMap

WeakMaps: Illustrated
ES6 WeakMaps, Sets, and WeakSets in Depth


#weakmap #垃圾回收 #garbage collection







Related Posts

BERT v.s. Gzip+kNN v.s. LGBM

BERT v.s. Gzip+kNN v.s. LGBM

763. Partition Labels

763. Partition Labels

[Day02] Pattern Matching

[Day02] Pattern Matching


Comments