
How to detect if an object has been garbage collected in JavaScript - luu
http://stevehanov.ca/blog/?id=148
======
neetodavid
Being able to automate tests for memory leaks is very helpful but I would much
rather incorporate the browser dev tools than not when debugging memory. Doing
it only in javascript feels very janky (weak maps, attaching giant array
objects to things, waiting ?? amount of time for the automated GC to run).

I was debugging memory leaks in some pretty large javascript products and
while I did initially start with methods like this post, I only started to
make reliable progress when I became familiar with the heap snapshot tool.
Diagnosing problem is trivial (just filter the heap snapshot by a Class name
and count the instances) and it is The Tool To Use to actually resolve it
(tracing the chain of references in the retainers view)

In the future if I need to write automated tests to identify memory leaks I
will look into automating the Dev Tools rather than fumbling with WeakMap or
performance.memory.usedJSHeapSize

~~~
hoten
I used the chrome devtools protocol to write a memory leak detector for my
game. Has helped me quite a bit. If you like I can share the source.

~~~
gargarplex
+1.

~~~
hoten
Replied to my comment with link.

------
wahern
It's a shame that WeakMap and WeakSet aren't enumerable. Ephemeron tables in
Lua are enumerable, so creating a data structure that detects when an object
is gone is possible. And because Lua supports finalizers, you can trigger an
event the moment the object is garbage collected.[1][2]

[1] Lua also supports reviving objects. For example, if your finalizer leaks
the object to the outside environment. Implementing this can be tricky, which
is perhaps why JavaScript doesn't permit it. So many limitations in JavaScript
exist because a particular feature would be too difficult to implement in
existing implementations.

[2] Of course, because Lua supports finalizers it's possible to detect GC
without using ephemeron tables. But if you can't control the target object's
finalizer, then ephemeron tables are handy for doing this. And you can use
your own finalizer on the ephemeron table value object to detect GC, though
it'll trigger one or two GC cycles behind when the key was collected.

~~~
jgraham
If you can observe GC from content then site behaviour will start to depend on
the specifics of one GC implementation. Not only will this be a disaster for
cross-browser interop, it will prevent that single engine improving its GC
design going forward because doing so will break working sites.

Note that this is somewhat different from a language like Lua where it isn't
necessary for all existing script to work with every new iteration of the
runtime without any changes.

The fact that implementation details like GC timing have remained behind the
veil of abstraction is one of the only reasons that JS engines have managed to
evolve from being relatively slow interpreters to highly optimised JIT
compilers.

~~~
klodolph
Apps already depend on the specifics of individual GC implementations. At
least, that’s my experience working with real-time apps.

~~~
jfkebwjsbx
Real-time and general purpose GCs does not sound like a good idea, except for
very specialized GCs...

~~~
klodolph
Talking about soft real-time here… do you think multimedia apps should be
native-only? That it’s a bad idea to put multimedia apps on the web?

~~~
jfkebwjsbx
The web does not imply a GC.

Most multimedia in the web is hardware accelerated, SIMD accelerated via
plugins or uses Wasm.

Any other way would be a huge waste on resources or simply impossible to do
for high resolutions.

------
bugmen0t
> Some browsers, including Chrome but not Firefox, have the ability to check
> the amount of Javascript memory used. So the solution to test if an object
> is there, is to make it sufficiently large so that it has a noticeable
> impact on memory.

It's not observable from content, but the Firefox profiler is awesome. Check
out [https://profiler.firefox.com/docs/#/./memory-
allocations](https://profiler.firefox.com/docs/#/./memory-allocations)

------
zomglings
It seems like the right way to do this is by modifying your runtime.

The author's solution proves their point about WeakMap but I don't see how I
would use it to practically detect memory leaks in my own programs.

Most importantly:

> If you are writing an application in Javascript, soon you will have to worry
> about memory leaks.

I wrote many applications in Javascript before I had to worry about memory
leaks.

------
nayuki
For reference, Java has PhantomReference and ReferenceQueue to let you detect
when an object has been garbage-collected:
[https://docs.oracle.com/en/java/javase/11/docs/api/java.base...](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ref/PhantomReference.html)
;
[https://docs.oracle.com/en/java/javase/11/docs/api/java.base...](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ref/ReferenceQueue.html)

There's a decent chance that JavaScript will eventually copy these features
and their design. So the current Java features offer a glimpse of what could
happen.

------
treve
There's also the WeakRef proposal: [https://github.com/tc39/proposal-
weakrefs](https://github.com/tc39/proposal-weakrefs)

~~~
syg
WeakRef and FinalizationRegistry will ship in Chrome 84.

~~~
Sophistifunk
I didn't know it was this close to production finally, thank Jebus!

------
cogman10
This is all stuff you really shouldn't do.

Use the profiling tools to figure out where your memory leaks exist. Doing it
in code has a lot of negative consequences. Weak maps and sets, for example,
will slow down your GC.

The typical usage of Weak maps and sets is for caching. This allows the GC to
give itself extra room on the heap at the expense of potentially another round
trip or new calculation.

------
Kiro
> If you are writing an application in Javascript, soon you will have to worry
> about memory leaks.

I run a massive Node application and never had any issues with memory leaks. I
wouldn't even know how to forcefully create one even if I wanted to.

I just code and never think twice about GC. How do people end up in situations
where they need to start using stuff like WeakMaps?

~~~
gnulinux
This doesn't make any sense. If you don't know how to create one, if you don't
know how to test that you don't have leak, _how_ can you know you don't have
leaks?

A simple python memory leak:

    
    
      a = get_a_huge_object()
      use_huge_object(a)
    
      # a function that takes long
      # that does not use a, but allocates more resources
      f()
    

You can fix this by doing:

    
    
      # get_huge_object() returns a context manager
      # it deallocates at the end
      with get_huge_object() as a:
        use_huge_object(a)
    
      f()
    

or even just:

    
    
      def g():
        a = get_a_huge_object()
        use_huge_object(a)
      g()
    
      f()
    

if you can rely on built-in deallocators.

I hardly believe javascript code is divinely protected against something like
this.

~~~
siscia
That python one is not a memory leak, it is just uncareful use of resources.

~~~
gnulinux
I don't consider this to be correct since the main computation in that
function will be done in `f()` where `a` is inaccessible and therefore cannot
be deallocated. This is similar to mallocing `a` and then not free'ing.
Obviously when the program dies python will deallocate everything, duh, that's
not a useful definition of memory leak since even in a memory leaking C
program, OS will clean up the memory after program dies. While the program is
working though, it's going to keep leaking memory this way even in python.

~~~
siscia
I am sorry but you are wrong. Your understanding of the concept is not
complete yet. Close but not complete.

Memory leak has a specific meaning. A memory leak is memory that is been
allocated but cannot be accessed and deallocate anymore. How useful is that
memory is not important. The Wikipedia article explains it much better than
myself:
[https://en.m.wikipedia.org/wiki/Memory_leak](https://en.m.wikipedia.org/wiki/Memory_leak)

There are useful definition of memory leak, some software need to run for a
very long time, they cannot leak memory.

In languages where the memory is automatically managed it is quite hard to
generate a memory leak.

Indeed is the original article that simply used a wrong terminology.

If you got more questions let me know! Happy to help!

~~~
gnulinux
I understand this definition, I explained it above in my comment. The problem
is that definition is not useful. It's useful to detect memory leaks this way
since it's easier, but that doesn't help us understand all problems our
program can have with leaking resources. First and foremost, the problem the
original definition points out doesn't exist any more. When a program dies,
all the resources it allocated, file descriptors, memory, ports etc will
safely be cleaned by the OS. Any modern OS that doesn't do so will consider
this a bug. So there is no practical reason to sweat the original problem.
It's still useful to sweat it since it's an _indication_ of a problem, which
is that your program fails to deallocate resources it allocates. However, this
failure is not important _just_ at the moment your program dies. It will
remain important the entire lifetime of the program. As I explaind above, if
your program runs in a loop, and needs to make O(N) resources runs on this
loop and you can use constant memory every time, all you really need is O(1)
memory since you can deallocate at the end of each loop. The problem arises
when you fail to do and use O(N) resources. This will make your program break
on large inputs when it really can work. What's more crucial is:

(1) Analyzing your program's minimum asymptotic resource need

(2) Observing your program's real asymptotic resource need

(3) Optimizing your program in a way (2) is closer to (1)

~~~
siscia
I just believe we work in very different environments.

We had problems with __compilers __having memory leak. The software I write,
runs for weeks or even months without being restarted. Yeah, the original
problem of memory leak, or even resources leaks in general is still very very
real in some field.

Now, of course if you use python or golang, or javascript, basically you will
never have a real memory leak. But this is not a good reason for calling bad
use of resources "a memory leak".

BTW: > First and foremost, the problem the original definition points out
doesn't exist any more. When a program dies, all the resources it allocated,
file descriptors, memory, ports etc will safely be cleaned by the OS. Any
modern OS that doesn't do so will consider this a bug. So there is no
practical reason to sweat the original problem.

Memory leak never concerns the OS, like never. When the OS allocate memory to
a software then is the software responsibility to deallocate it, returning it
to the OS.

> Any modern OS that doesn't do so will consider this a bug.

This is true but it is not what we are discussing.

Anyhow, I am just trying to help you understand what people usually means with
"memory leak" because you seems a little confused.

But if you are sure and you are definitely not confused and you think that it
is me being wrong, I am not going to engage in any discussion.

Cheers,

------
syspec
You can do this explicitly with puppeteer. Given a prototype it can tell you
how many love instances of that prototype are currently in the heap.

For the authors use case this seems like it would work, since they mention it
is part of their test suite

------
himinlomax
I was confused by his description of WeakMap, because I hadn't looked at the
spec in a while. For reference, the reason why it's not applicable to the task
at hand is because it's not primarily meant to have the _values_ weakly
referenced. Rather, it's meant to not lock the _keys_ in. I'm not sure what he
means by "best used to link objects together". WeakMap simply allows you to
keep data about an object outside of it and without keeping it from being
freed when it's not needed anywhere else.

~~~
sfink
That's what he means by linking objects together. Though I would describe it
as you did; WeakMaps are normally used for associating data with an object,
without having the WeakMap entry itself keep the object (aka the WeakMap key)
alive. If the object goes away otherwise, the entry will go with it. (Or if
the WeakMap goes away, then all of its entries will go away at once.) In terms
of "liveness", a WeakMap entry is an "and" edge between the key and the map:
the entry is only kept alive if _both_ the key and map are still alive. (And
the entry keeps the value alive, though it may not be the only thing doing
so.)

WeakMaps (ephemeron tables) were designed specifically to make garbage
collection _not_ be observable, for the reasons jgraham described above.

If you really want to know, such as for testing, then WeakRef and
FinalizationRegistry are the newer ways to do this. But know that if your app
needs this stuff _as part of its normal functioning_ , then there's at least a
90% chance you're doing something wrong. The GC does not run because you think
it should run. If anything, it tries to not run, as much as possible. And the
more we optimize it, the better it will get at not running. "Running slow
code" is worse than "running fast code" is worse than "not running code at
all".

(source: I work on the SpiderMonkey GC engine.)

------
winrid
Is there anything like VisualVM for NodeJS? It's nice in JVM land that you can
just see how many objects of a particular class are on the heap.

Chrome's devtools kind of work, but it's not nearly as good.

I'd love to be able to see how many objects exist with a certain signature, or
prototype. I found some mdb_v8 thing but it seems it's built for a very old
version of V8.

~~~
alex7o
I haven't tired that yet, but it should be possible to use graal.js, which is
integrated with node. The should allow using visual VM on node.

~~~
MH15
This looks very interesting... NodeJS is decently performant out of the box
but I'd love to have the capability to run advanced profiling tools.

------
gwbas1c
> If you are writing an application in Javascript, soon you will have to worry
> about memory leaks.

What? No! Memory leaks are impossible in fully garbage collected environments.
A memory leak is when you manually allocate memory and loose the pointer, thus
making it impossible to access the memory. (Because there's no pointers in
Javascript, there's no memory to leak.)

What's happened is not a memory leak, it was an unregistered event handler.
These are best diagnosed with profilers, not gymnastics with weak references.
(It's not a memory leak because there is still a valid reference through the
event handler, ie, no lost pointer.)

~~~
jonathanlydall
Even if there is a more correct technical term for this, I think you’re trying
to fight back an ocean here.

If you have a function which each time inadvertently creates a new object in
memory which it never de-references (thus is never GC’d), repeated use of the
function will ultimately result in memory exhaustion and termination of the
process.

Everyone I’ve ever met would refer to that as a memory leak.

------
alkonaut
I was assuming a WeakMap is just a map (hash table) from a key to a WeakRef?
What is it if it isn't that (Explain like I'm a 5 year old person who doesn't
understand dynamic types or JS)? I read here that WeakRef is only a proposal
yet, so clearly a WeakMap/WeakSet can't make use of a WeakRef...

~~~
stestagg
It’s a map from a ‘WeakRef’ to a value. So if you have an object reference,
you can associate a value with that object. But not the other way round

~~~
alkonaut
Thanks. That does feel a bit backwards, for the purpose of GF tracking as you
can’t store id->obj.

I assume if/when a WeakRef is exposed in the language then a normal map can be
used to store id->WeakRef(obj), so no real need them for also having a
“mapWithWeakRefValues”?

~~~
ghusbands
A WeakMap works with objects as keys. You can do it with objects you don't
control, without adding some sort of id field. It can't be fully simulated
with a WeakRef.

One common use is to associate extra data with objects, without attaching it
to those objects directly. In your map, you add an obj->extradata mapping.
Having the target be a WeakRef would just mean you'd lose your extradata while
the source obj is still around.

------
coopsmgoops
I was under the impression that if you have a reference to an object it won't
be GCed. Is that not the case?

~~~
stevula
Check out WeakMap and related weak- data structures.
[https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap)

------
Vanit
TLDR; you can't.

~~~
hoten
You can, you just need to step up a level of abstraction and use the debugger
protocols in v8.

------
chrismsimpson
To write a language with only borrowing would be quite easy, everyones just to
lazy too do it

