Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does it make sense to attempt to assist the JavaScript Garbage Collector?

As I more heavily use JavaScript like a high-level object-oriented language, I find myself thinking like the C/C++ programmer that I am when I'm finished with objects. I know the GC is going to run eventually and clean up my mess, but are there things I can do to actually help it along?

For example, I have an array of large/complex primary objects... each primary object might have arrays and other subsidiary object references inside. If I'm done with a primary object and just remove it from the array, the GC will presumably eventually figure out everything else that object pointed to all by itself, circular internal references and all. But does it make sense when removing the primary object from the storage array to go through it and array.length=0 any arrays and reference=null any objects to basically make the GC job easier (e.g. explicitly removing references means less for the GC to track)? Kind of a manual destructor if you will. Is it worth it to do that or am I wasting time/effort for little/no gain?

I suppose this is more of a general theory of GC question (Java etc. too) but I'm primarily interested in JavaScript for the purposes of this question.

Thanks!

like image 451
mark Avatar asked Dec 20 '12 19:12

mark


1 Answers

This may depend on the specific garbage collector, so an answer would depend on which JavaScript engine you're using.

First, I'll note that the best application code does two things. It achieves its technical goals of functionality and performance, and it is resilient to change. Additional code should serve enough of a purpose to justify the added complexity.

With that said, according to the Google's notes on V8, which is the JavaScript engine used by Chrome,

V8 reclaims memory used by objects that are no longer required in a process known as garbage collection. To ensure fast object allocation, short garbage collection pauses, and no memory fragmentation V8 employs a stop-the-world, generational, accurate, garbage collector. This means that V8:

  • stops program execution when performing a garbage collection cycle.
  • processes only part of the object heap in most garbage collection cycles. This minimizes the impact of stopping the application.
  • always knows exactly where all objects and pointers are in memory. This avoids falsely identifying objects as pointers which can result in memory leaks.

In V8, the object heap is segmented into two parts: new space where objects are created, and old space to which objects surviving a garbage collection cycle are promoted. If an object is moved in a garbage collection cycle, V8 updates all pointers to the object.

Generational garbage collectors tend to move objects between heaps, where all live objects are moved into a destination heap. Anything not moved to the destination is considered to be garbage. It's not clear how the V8 garbage collector identifies live objects, but we can look at some other GC implementations for clues.

As an example of the behavior of a well-documented GC implementation, Java's Concurrent Mark-Sweep collector:

  1. Stops the application.
  2. Builds a list of objects reachable from the application code.
  3. Resumes the application. In parallel, the CMS collector runs a "mark" phase, in which it marks transitively reachable objects as "not garbage". Since this is in parallel with program execution, it also tracks reference changes made by the application.
  4. Stops the application.
  5. Runs a second ("remark") phase to mark newly reachable objects.
  6. Resumes the application. In parallel, it "sweeps" all objects identified as garbage and reclaim the heap blocks.

It's basically a graph traversal, starting at a set specific nodes. Since disconnected objects aren't accessible, their connectedness to other disconnected objects shouldn't come into play.

There's a good, if somewhat dated, white paper on the way Java garbage collection works at http://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf. Garbage collection isn't unique to Java, so I'd suspect some similarity between the various approaches taken by Java virtual machines and other runtimes such as JavaScript engines.

Raymond Chen wrote a blog post that pointed out that walking memory that you're about to free can have a negative impact on performance. The context was around freeing memory manually on application shutdown. Since blocks could have been swapped to disk, the act of traversing references can cause those blocks to be swapped in. In this case, the program is doing a traversal of blocks that would have just been marked as available and left untouched.

So in a situation where the OS may have swapped out some of the blocks, the act of "assisting" the garbage collector, especially on longer-lived objects, may end up slowing things down.

And if you're not generating enough data to be concerned about swapping out, then a reasonable garbage collector wouldn't take long enough to notice.

So chances are it's not worth the effort, and could be counterproductive. Although it probably makes sense to drop the references to the heap "dominators". These are the objects that, if collected, would allow the collection of many other objects. So drop the reference to the collection itself, but not to each item within the collection.

like image 73
GargantuChet Avatar answered Oct 11 '22 12:10

GargantuChet