4

It is my understanding of WeakMap that "References to objects in the collection are held weakly. If there is no other reference to an object stored in the WeakMap, they can be garbage collected."

Why do the following key/value pairs still appear in the WeakMap after the references have been removed? Shouldn't the WeakMap be empty?

let dog1 = {name: 'Snickers'};
let dog2 = {name: 'Sunny'};

var strong = new Map();
var weak = new WeakMap();


strong.set(dog1, 'Snickers is the best!');
strong.set(dog2, 'Sunny is the 2nd best!');
weak.set(dog1, 'Snickers is the best!');
weak.set(dog2, 'Sunny is the 2nd best!');

dog1 = null;
dog2 = null;

console.log(strong);
console.log(weak);

/*
Output
Map(2) {{…} => "Snickers is the best!", {…} => "Sunny is the 2nd best!"}
WeakMap {{…} => "Snickers is the best!", {…} => "Sunny is the 2nd best!"}
*/

setTimeout(function(){
console.log("1200ms later... waiting for garbarge collection");
console.log(strong);
console.log(weak);
}, 1200);

/*
Output
Map(2) {{…} => "Snickers is the best!", {…} => "Sunny is the 2nd best!"}
WeakMap {{…} => "Snickers is the best!", {…} => "Sunny is the 2nd best!"}
*/
dandan
  • 1,016
  • 8
  • 16
  • 4
    You still have a reference in `strong`, so they can't be garbage-collected. – Amadan Feb 06 '20 at 03:17
  • "*How to observe…*" - you can't. Garbage collection is not guaranteed to run, even after you fix your bug. – Bergi Feb 06 '20 at 03:22
  • @Bergi There's no guarantee, but the above seems fairly consistent at exhibiting the garbage collection behavior once the bug is fixed. It's fine for learning purposes (which seems to be what this question is about) as long as one doesn't rely on it to work in production code. – JLRishe Feb 06 '20 at 03:32
  • @JLRishe Does it now? They must have changed that, [it wasn't observable in `console.log`'d `WeakMap` objects before](https://stackoverflow.com/q/49841096/1048572). – Bergi Feb 06 '20 at 12:21
  • @Bergi If I delete the second `strong.set` line and run the code in Chrome's console, the WeakMap is shown as containing dog2 prior to the `setTimeout` and not containing it after the timeout. The question you linked to doesn't use a timeout, so the code in that question is probably logging the weakmap before it can be garbage collected. – JLRishe Feb 06 '20 at 12:35
  • @JLRishe Ah, I guess the `setTimeout` makes the difference. – Bergi Feb 06 '20 at 12:36
  • @Bergi Right. I believe that most likely the GC doesn't run until the call stack is clear. – JLRishe Feb 06 '20 at 12:42

3 Answers3

1

After fixing the bugs pointed out by @Amadan @Bergi @Geeganage, the following code gives the desired output in Safari and Chrome, with garbage collection running after one of the several newly added timeouts. Resulting in the final WeakMap not holding onto any references.

let dog1 = {name: 'Snickers'};
let dog2 = {name: 'Sunny'};

var strong = new Map();
var weak = new WeakMap();


strong.set(dog2, 'Sunny is the 2nd best!');
weak.set(dog1, 'Snickers is the best!');

dog1 = null;
dog2 = null;

console.log(strong);
console.log(weak);

/*
Output
[Log] Map {{name: "Sunny"} => "Sunny is the 2nd best!"} (1)
[Log] WeakMap {{name: "Snickers"} => "Snickers is the best!"} (1)
*/

setTimeout(function(){
console.log("1200ms later... waiting for garbarge collection");
console.log(strong);
console.log(weak);
}, 1200);

setTimeout(function(){
console.log("3200ms later... waiting for garbarge collection");
console.log(strong);
console.log(weak);
}, 3200);

setTimeout(function(){
console.log("6200ms later... waiting for garbarge collection");
console.log(strong);
console.log(weak);
}, 6200);

setTimeout(function(){
console.log("12200ms later... waiting for garbarge collection");
console.log(strong);
console.log(weak);
}, 12200);

/* 
Output (eventually)
[Log] Map {{name: "Sunny"} => "Sunny is the 2nd best!"} (1)
[Log] WeakMap {} (0)
*/

Since garbage collection is not guaranteed to run, if you paste the code into the console while scrolling a JS heavy website, GC may actually run after the first timeout.

dandan
  • 1,016
  • 8
  • 16
  • 1
    This is still not guaranteed to show you your desired results, garbage collection happens when ever JS engine sees it is time to do so ... we can not define the time it does it. – Ershad Qaderi Mar 14 '21 at 08:55
  • I tried this same example and got the result described (I see weak getting empty at some point). I understand that I cannot determine when garbage collection will happen, but I can be sure that it will happen at some point, right? I modified this example and only changed all the `setTimeout`s for just one `setInterval` and result I get is that with `setInterval` the weakmap never gets garbage collected, and with `setTimeout` it always does (sometimes in the first one, sometimes in the third one, etc) Does `seInterval` somehow creates strong references? – Pablo Meni Aug 16 '22 at 13:40
1

Your code is totally fine, the problem is that it is not guaranteed to show you your desired results, garbage collection happens when ever JS engine decides that it is time to do so ... we can not define the time it does it. That's exactly why WeakMap does not support iteration and methods keys(), values(), entries(), because we don't know if those keys and values are there or not ... in other words we don't know if garbage collection has happened at that moment or not. If we have a loose reference, and it has not been garbage collected yet, these methods give us wrong results, and that's why they are disabled for WeakMap

Ershad Qaderi
  • 441
  • 4
  • 11
0

In here you firstly created two seperate javascript objects called(reference variable names) dog1 and dog2:

    let dog1 = {name: 'Snickers'};
    let dog2 = {name: 'Sunny'};

And then you've created two different Map objects called strong and weak.

    var strong = new Map();
    var weak = new WeakMap();

In here you only set the value of the reference variable as the key for both Maps. And that map object is a seperate object and it doesn't have any connection with those reference variables "dog1" and "dog2" anymore after those values are put as the keys of that map objects.

   strong.set(dog1, 'Snickers is the best!'); // Here after value of dog1 doesn't affect map "strong"
   strong.set(dog2, 'Sunny is the 2nd best!'); // Here after value of dog2 doesn't affect map "strong"
   weak.set(dog1, 'Snickers is the best!'); // Here after value of dog1 doesn't affect map "weak"
   weak.set(dog2, 'Sunny is the 2nd best!'); // Here after value of dog2 doesn't affect map "weak"

So, after put the values of these variables in to the maps, even you assign null value to those reference variables it doesn't affect the map. Because they are seperate objects and don't depend on each other anymore.

   dog1 = null;
   dog2 = null;

So, even you check it after 1200ms also the result is same. Because making changes(by assigning null) to those reference variables doesn't affect the map.

   setTimeout(function(){
     console.log("1200ms later... waiting for garbarge collection");
     console.log(strong);
     console.log(weak);
   }, 1200);

And also you still have a reference variables strong and weak,so they are not still elegible for garbage collection.

Hope that following link will help you to learn garbage collection in JavaScript:

https://dzone.com/articles/memory-management-and-garbage-collection-in-javasc

  • This is completely not true, the only issue here is that we don't know when does garbage collection happen. We only know that WeakMap will let go of unreferenced keys when the engine does garbage collection – Ershad Qaderi Mar 14 '21 at 08:58
  • What if I delete the key from the Map, i.e. map.delete(key), or map.clear()? Will that key be garbage-collected afterwards? – Uahnbu Tran Jul 19 '21 at 03:37