Jonas: Welcome back to part two of Memory Management.
And this one is all about how memory is released.
In particular, we're gonna dive deep into
how a mechanism called garbage collection works
behind the scenes in order
to keep our memory clean and healthy.
So remember how in JavaScript each value that we create goes
through a memory lifecycle where first memory is allocated
for the value, then the allocated piece of memory is used.
And finally, when no longer needed,
the piece of memory is released.
Now, we already talked in depth about step one,
step two is pretty obvious,
so there's actually nothing to learn there,
and so let's now turn our attention to the way
in which memory is released
in the JavaScript engine in step three.
Basically, we're gonna answer the question,
how is memory freed up
after we no longer need a certain value?
Now, since these values are stored in the stack
and the heap, we need to analyze
both these storage mechanisms.
So first off, in the call stack, it's all very simple
because the variable environments
where primitives are stored are simply deleted
when the corresponding execution context pops off the stack.
As an example, here is the call stack
with the global execution context
and two additional ones for two function calls.
The global execution context has the X variable,
which is clearly a primitive, and the execution context
of the calcAge function has variables Y and Z.
So these three variables are clearly stored
in the call stack.
And so then as the execution context pops off the stack,
the variable environment of the calcAge function
will just be removed from memory
together with the execution context
that just popped off the stack.
So as I said, very simple, right?
Variables are simply stored
in an execution context in the call stack,
and as soon as the context is gone, so are the variables,
and the memory that they occupied
is simply released for future usage.
Now, notice what happens with the X variable
in the global execution context.
Since this execution context never disappears,
this value will stay in the stack forever.
Now, this is no big deal, I just wanted to make it clear
that global variables will of course never be deleted,
which is pretty intuitive, I think.
Okay, but now let's move on to the heap
where memory management is considerably more complex.
In order to delete old unused objects from the heap
and free up memory, JavaScript engines employ a process
called garbage collection.
This garbage collection is the central tool
for memory management in any JavaScript engine.
And notice how I said engine here,
because it's indeed the engine
that runs garbage collection automatically
whenever it sees fit.
We developers cannot control when the heap memory
is cleared by garbage collection,
which is actually a great thing,
because this automatic memory management
makes our lives a lot easier.
So you can basically think of garbage collection
as an automatic cleaning service that comes into your house
from time to time and identifies and removes old stuff
that no one is using anymore.
But anyway, there are different ways
to implement garbage collection,
but all modern engines use an algorithm
called mark-and-sweep.
And so let's now quickly check out how it works
by bringing back the call stack and heap.
Now, first of all, here in red and green
we have objects called hobbies and tasks,
and so as we just learned previously,
these variables actually store references
to the actual object stored in the heap.
Let's now also say that the red object references
some other object, which, of course,
is also stored in the heap,
and the green object references just another object.
And by the way, I keep saying objects
that are stored in the heap,
but as we just learned earlier,
these can actually be not just objects,
but also erase, functions,
and other data structures like sets or maps.
Now, okay, but now back to the mark-and-sweep algorithm.
The first step of garbage collection
with the mark-and-sweep algorithm is the mark phase
where all objects that are reachable
from a so-called root are marked as alive.
Now, roots are basically starting points
from which the algorithm starts to look
for alive or reachable objects.
And different things can be roots,
but the most obvious ones are the global execution context,
which is always present,
and any other execution context of running functions.
So again, the algorithm starts looking for objects
in these places called roots.
In this example, the green and red objects
are obviously alive because they can be reached
from one of the roots.
And the same is true for the blue and gray objects.
They're also alive because they can also be reached
from one of the other reachable objects.
But now let's take it one step further,
because an object can be reached
by more than just the global execution context
or one of the other contexts in the stack.
They can also be reached by event listeners or active timers
or by something that we call closures.
And more about closures later.
So these two are also roots
for the mark-and-sweep algorithm.
And now let's say that we have this purple object
that is referenced and therefore reachable
from an active timer and this yellow object
that is reachable from a closure.
So both these are alive as well.
And now finally, just to make this a bit more complete,
let's imagine that we also have this pink object
in the heap, which isn't being referenced by anything.
And so this one is not marked as alive
because it cannot be reached by any of the roots.
So we can basically say that this one is dead.
Okay, so with this,
we basically just simulated the mark phase.
So phase one in the mark-and-sweep algorithm.
The second phase is the sweep phase
where all the unmarked objects, or in other words,
all the unreachable objects, are simply deleted.
Basically, the algorithm has decided in the first step
that these objects are no longer needed
and that the memory occupied by them can be reclaimed
and used for future memory allocations for future objects.
So to simulate this in our example here,
right now we only have one object that is unreachable,
and that's the pink object.
So what's gonna happen in this phase?
Well, that's right, the pink object will be deleted
and the memory will be reclaimed.
And so just like this, the automatic garbage collection
has insured that our memory stays nice and clean
and not cluttered with objects
that can no longer be accessed anyway
and that we therefore no longer need in the heap.
All right, but now what happens
when the getTasks function finishes execution
and its context pops of the stack?
Well, let's simulate that our garbage collection runs again.
And again, I want to just emphasize
that there is no way to control
or trigger garbage collection manually from our code.
We also have no way of knowing when
and how often garbage collection happens.
It depends on a few factors like
how much memory our app is consuming,
how much memory is available,
which JavaScript engine the browser is using, and so on.
But anyway, for the sake of this example,
let's say the garbage collection algorithm
runs again at this point.
So now with the getTasks variable environment gone,
there's really no one
to reference the green object anymore, right?
Therefore, the green object and also the blue object
are no longer reachable from any root.
They are no longer alive.
Therefore, as the process moves into the sweep face,
both the green and the blue objects will be swept away
and the memory that they occupied will be reclaimed.
Okay, but now let's turn our attention to the red object,
and let's ask ourselves the question,
how could this object ever be deleted?
Well, the answer is that it can't.
The red object will never be deleted,
so it will stay in the heap forever
because the global execution context will never disappear.
So this root will always exist
and we'll always be able to reach the red object.
This means that any globally defined object
will never be garbage collected,
even if we no longer need it in our code.
And what about the purple and yellow objects?
What if our app also no longer needs those objects?
Well, this idea of an object being no longer needed
actually brings us to our final topic of this lecture,
which is memory leaks.
A memory leak happens when an object
that is actually no longer needed by our application
is incorrectly still reachable by the garbage collector
from one of the roots.
As a result, the object is marked as alive
and is not deleted, again,
even though we actually no longer need it in our code.
So it should be deleted, but it can't.
And you can think of a memory leak
basically like forgetting to throw away
some stuff that you no longer need.
And so this will then clutter up your house unnecessarily.
But back to JavaScript, this happens when an object
is still incorrectly referenced from somewhere.
And one major source of these wrong
and undesired references are old
and unnecessary event listeners and timers.
If, for example, a timer creates an object,
this object will always be reachable
unless the developer actively deletes the timer
when they no longer need it.
Otherwise, if the timer just stays around
and keeps running, it will forever reference
the unnecessary object, causing a memory leak.
The same is true for an event listener
that might no longer be needed at some point.
So in order to avoid memory leaks,
make sure to always deactivate timers
and event listeners when they're no longer required,
especially if they reference large objects.
Also avoid declaring large objects as global objects,
because these will also never be garbage collected,
as we just saw earlier.
So that's gonna lead to memory leaks as well.
Now, memory leaks can be a huge topic, but there's no need
to dig even deeper into it at this point.
This is a brief overview of
how the garbage collection process works.
It's gonna run over and over inside the user's browser
while they're using the application,
making sure that memory stays clean and uncluttered
of objects that are no longer necessary over time.
Now, in reality, memory management
is way more complex than just this.
It's an extremely complicated process.
There are actually multiple heaps,
objects are moved from one heap
to another depending on their age,
the garbage collector has multiple algorithms
for the different heaps like generational garbage collection
and so on, but I'm absolutely sure
that this broad overview that you just learned about
is more than enough at this point.
So this already gives you a great idea of how memory
is automatically managed inside the engine.
And now before we move on, I need
to quickly mention four more big topics
about how JavaScript works behind the scenes
that are not in this section
but closer to where we actually need to learn about them.
The first one, which I actually just mentioned
in the last slide, is closures.
We're gonna dive really deep into closures
once we reach the Closer Look at Functions section.
Next, one of the most fundamental concepts
of JavaScript is prototypal inheritance,
but we're only gonna talk about that
in the Object Oriented Programming section of this course
because it doesn't make sense to learn about this now
only to forget it all until we finally reach that section.
The same is true for a detailed lecture on the event loop.
We already introduced the event loop in this section,
but in section Asynchronous JavaScript,
we'll dive really deep into
how exactly this event loop works
and why it's such a fundamental piece
of the JavaScript engine.
Finally, we'll have a few lectures on
how the DOM actually works behind the scenes
in the Advanced DOM and Events section as well
so that you can then apply what you learn right away.
And with this, we actually finished this huge section about
how JavaScript actually works behind the scenes.
It was a pretty long one with so many new concepts to learn,
and many of them were hard and probably confusing,
but again, that's really not a problem.
Hard and confusing concepts are always a part of learning,
and even if you did not understand 100% of everything,
you're still good to move on in the course
to the next section now.
Just reaching the end of this section
already gives you a huge advantage
over many other developers who have no idea
about many of the things that I just showed you here.
So congratulations for sticking with it to the end
and for learning all of this valuable knowledge.
And now with that, take a break
and I'll see you in the next section.