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/plex 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!
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/plex 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!
Share Improve this question edited Dec 20, 2012 at 19:12 Hanlet Escaño 17.4k10 gold badges53 silver badges76 bronze badges asked Dec 20, 2012 at 19:10 markmark 5,4692 gold badges23 silver badges35 bronze badges 1- I find this answer very interesting: stackoverflow./a/4324162/752527 – Hanlet Escaño Commented Dec 20, 2012 at 19:14
3 Answers
Reset to default 6This 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 plexity.
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:
- Stops the application.
- Builds a list of objects reachable from the application code.
- 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.
- Stops the application.
- Runs a second ("remark") phase to mark newly reachable objects.
- 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 e into play.
There's a good, if somewhat dated, white paper on the way Java garbage collection works at http://www.oracle./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.
Rarely.
Which is different from never.
Generally, if you develop in a reasonable manner, unused objects will be falling out of scope appropriately and the Garbage Collector will simply work as expected. And unless you really understand what you're doing, efforts to help the Garbage Collector do its job often don't work as intended. Garbage Collection is an area that has undergone significant optimization by the creators of the various Javascript engines. Generally, you shouldn't mess with it unless:
- You have proven to yourself with hard numbers from analysis tools that there is a real need, and
- You can demonstrate real understanding of what actually needs to be change in order to help the Garbage Collector.
Neither of these is easy to achieve.
So maybe the best answer is that unless you're already advanced enough to understand the exceptions, you probably shouldn't be worrying about this.
Very smart people have worked very hard on those garbage collection models. The odds that anything you do is going to massively improve performance over what they implemented is relatively low.
If garbage collection is being a major cost for you, you're likely better off focusing on reducing the number of objects that have to be garbage collected (consider Object Pool Pattern)