— title: Garbage Collection in JavaScript date: 2017-11-02 12:44:59 lang: en tags: JavaScript category: JavaScript — Memory management in JavaScript is performed automatically and invisibily to us. We create primitives, objects, functions… All that
takes memory. What happens when something is no needed any more? How does the JavaScript engine discover it and clean it up? ## Reachability The main concept of memory management in JavaScript is reachability. “Reachable” values are those that are accessible
or useable somehow. They are guaranteed to be stored in memory.
Roots There is a set of inherently reachable values that cannot be deleted for obvious reasons, those values are called roots - Local variables and parameters of the current function. - Variables and parameters for other functions on the current
chain of nested calls. - Global variables - (There are some other, internal ones as well) Any other values is considered reachable if it’s reachable from a root by a reference or by a chain of reference. For instance, if there’s an object(A) in a local
variable, and object(A) has a property referencing another object(B), then object(A) is considered reachable, and the object(B) is also reachable. >There’s a background process in the JavaScript engine that is called garbage collector. It monitors
all objects and remove those that have becomen unreachable. ### A simple example Here’s the simplest example: javascript // user has a reference to the object let user = { name: "John" };
Here the arrow depicts an
object reference. The global variable user
references the object {name: "John"}
, the name
property stores a primitive, so it’s painted inside the object. If the value of the user
is overwritten, the reference is lost: javascript user = null;
Now the object becomes unreachable. There’s no reference to it. Garbage collector will junk the data and free the memory. ### Two references Now assume we copied the reference from user
to admin
: javascript //
user has a reference to the object let user = { name: "John" }; let admin = user;
Now if we do the same: javascript user = null;
the object is still reachable via admin
global variable, so it’s still in memory.
If we overwrite admin
too, the it can be removed by garbage collector. ### Interlinked objects A more complex example: javascript function marry(man, woman) { woman.husband = man; man.wife = woman; return { father: man, mother: woman } } let family
= marry({ name: "John" }, { name: "Ann" });
Function marry
takes two objects and “marries” the two objects and returns a new object that contains them both. Global variable family
is assigned to function marry
which means also assigned to an
object which contains a father
object and a mother
object. The resulting memory structure: Now, all objects are reachable. Let’s remove two reference: javascript delete family.father; delete family.mother.husband;
Now there is no reference to object father
any more, so we could say that object father
is now unreachable. It needs to delete the both two references to make father
unaccessible. What
if we set family
variable to null
: javascript family = null;
then the memory structure becomes: It’s obvious that father
and mother
objects are still linked, but their common parent object is now unlinked from
the root, there is no reference to it any more, so the whole object(returns of function marry
) becomes unreachable and is removed from the memory. ### Internal algorithms The basic garbage collection algorithm is called “mark-and-sweep”. The following
“garbage collection” stpes are regularly performed: - The garbage collector takes roots and “marks” (remember) them. - Then it visits and “marks” all references from roots. - Then it visits the marked objects and marks their references. All visited
objects are remembered, so as not to visit the same object twice in the future. - …And so on, until there are unvisited references. - All objects without the “mark” are removed. For instance, let our object structure look like this: There are 3 global variable A,B,C
, those are also roots
. Now let’s see how “mark-and-sweep” garbage collector deals with it. The first step marks the roots: Then marks their references:
And their references, while possible, until all references are marked: Now the objects that could not be visited in the process are considered unreachable and will be removed. That’s the concept
of how garbage collector works. JavaScript engines apply many optimizations to make it run faster and not affect the execution. Some of the optimizations: - Generational collection - objects are split into two sets: “new ones” and “old ones”. Many
objects appear, do their job and die fast, they can be cleaned up aggressively. Those that survive for long enough, become “old” and are examined less often. - Incremental collection - if there are many objects, and we try to walk and mark the whole
object set at once, it may take some time and introduce visible delays in the execution. So the engine tries to split the garbage collection into pieces. Then the pieces are executed one by one, separately. That requires some extra bookkeeping between
them to track changes, but we have many tiny delays instead of a big one. - Idle-time collection – the garbage collector tries to run only while the CPU is idle, to reduce the possible effect on the execution. ## Summary - Garbage collection is performed
automatically. We cannot force or prevent it. - Object are remained in memory while they are reachable. - Being referenced is not the same as being reachable(from a root). Modern engines implement advanced algorithm of garbage collection. A general book
“The Garbage Collection Handbook: The Art of Automatic Memory Management” (R. Jones et al) covers some of them. More detailed information about V8 garbage collector is in the article A tour of V8: Garbage Collection.
————————- Reference: JavaScript.info