A module in node.js is simply a closure that stays alive by the same rules that other closures in Javascript would stay alive. As long as any code within the closure is still reachable by other code, then the closure itself can't be garbage collected. And, modules are also cached by the module loader which means a reference to the module is kept alive in the module cache, even if no other code has retained a reference to the module. You may find it helpful to read this article: How require() Actually Works because it is normal Javascript GC of a scope that determines when a given module can be garbage collected or not. There is no special garbage collection for modules.
So, your module variables will be alive as long as this module closure created when the module was loaded stays referenced. While that closure is alive, the only way that contents of the referenced variables within the module would be freed while the module was loaded is if you clear the contents of those variables manually (e.g. setting to null
).
By default, a node module remains alive and loaded in the node module cache (waiting for some other code to require()
it in again) even if it is no longer currently being used or referenced by the rest of your code. It can be manually removed from the cache if you so choose. See this answer for details on manually removing a module (and perhaps any modules that it also loads) from the cache: Unloading node code/modules. So, if none of your code has a reference to the module, the module has no live event handlers or callbacks in it and you've manually removed the module from the cache, then the module closure should no longer have any reachable code references into it and the GC can free that whole scope (and thus the module).
A module remains alive as long as any code in it is still reachable (e.g. can still be called by any other code). In your case, this would be the case if any other code still has a reference to the module or as long as the event handler in the module is still alive (could still be invoked) because the event handler callback references code within the module. It is not always clear exactly how smart a given garbage collector will be about knowing when a given event handler is done and can never be invoked again in the future.
In your specific example, the $(window).scroll()
event handler (which seems like a bit of a made up example because there's usually no window
object in node.js that does scrolling) would theoretically be alive forever until you manually removed the event handler with .off()
or until the window
object itself is deleted or something like that. So, the references inside that event handler would never go away on their own.
Other event handlers that have a specific lifetime such as an Ajax success handler will be done when the ajax call itself has finished executing and all callbacks have been called. Those event handlers will release any references they hold when they are done. Same for a setTimeout()
. It will release any reference it holds when it executes (or when the timer is cancelled).
It is often times hard to predict how smart a garbage collector can be and when it will realize that a given variable is no longer reachable and thus can be garbage collected. Some things are easy to understand such as when a variable goes out of scope and no other references remain to the scope so the entire scope will be GCed. But some things are not so simple such as when a scope is still alive because an event handler in that scope is still alive, but nothing in that particular event handler could actually reference a given variable within that scope. Whether or not the GC will actually try to GC a single variable within that scope in that case is implementation dependent. Things like eval()
and constructing new Function
objects with code built via string manipulation make it very hard for Javascript to know exactly what could and could not be referenced in the future from a given event handler or callback (since it's possible to construct almost any reference programmatically without the interpreter knowing what you "may" reference in the future). This complicates fine grained garbage collection. What you can count on is whole scope garbage collection (when the whole scope is released). Counting on finer grained GC than that it probably not wise. If you are explicity done with a very large variable within a scope that might last a lot longer, then it's safer to just null
out that very large variable so its specific reference to the large data is cleared when you want it to be cleared. This wouldn't be significant for a small string (unless you had tens of thousands of these objects), but might be relevant for a large buffer or very large string.
Edit: It does appear that V8 does garbage collection of individual variables within a scope if those variables themselves are not referenced within any of the code that is still reachable within the closure and there are no uses of eval()
in that same code. I have not found any authoritative references on the subject, but have verified that this appears to be the case in testing actual situations.