How Does JavaScript Really Work? (Part 2)

How the JavaScript V8 engine works with memory management, call stacks, threads, and event loops.

In my last article, I gave a brief overview of how the programming language works and the details of the V8 engine. This article will cover some important concepts that every JavaScript programmer must know, not just the V8 engine.

Time complexity and space complexity are two of the concerns of all programmers. The previous article covered V8’s speed and optimization section to improve JavaScript execution times, which focused on memory management.

memory

Orinoco logo: V8 garbage collector

  • Whenever you define a variable, constant, object, etc., in JavaScript, you need some place to store it. That place is memory.
  • When you encounter a statementvar a = 10, memory will be allocated a location for storageaThe value of the.
  • Available memory is limited, and complex programs can contain many variables and nested objects. Therefore, it is important to use available memory wisely.
  • Unlike languages like C, which require explicit allocation and freeing of memory, JavaScript provides automatic garbage collection. Once an object/variable is taken out of context and no longer used, its memory is reclaimed and returned to the free memory pool.
  • In V8, the garbage collector, called Orinoco, does the above process efficiently.

Mark scan algorithm

Mark scan algorithm

To determine objects that can be safely removed from memory, this simple and efficient algorithm is used. The name of the algorithm describes how it works; Marks objects as accessible/inaccessible and clears unreachable objects.

The garbage collector periodically starts with root or global objects, walks through the objects they reference, walks through the objects those references reference, and so on. Then all unreachable objects are cleared.

A memory leak

While garbage collection is efficient, it does not mean that developers can leave memory management alone. Managing memory is a complex process, and determining which chunk of memory is not needed cannot rely entirely on algorithms.

A memory leak is a portion of memory that was used by a program and is no longer used, but is not returned to the memory pool.

Here are some common errors that cause memory leaks in your programs.

Global variables: If you keep creating global variables, they will always exist during program execution even if you don’t use them. If these variables are deeply nested objects, they can waste a lot of memory.

var a = { ... }
var b = { ... }
function hello() {
  c = a;  // this is the global variable that you aren't aware of.
}
Copy the code

If you access an undeclared variable, a variable is created globally. In the example above, c is the global variable/global object that you didn’t implicitly create using the var keyword.

Event Listeners: This may happen when you create a lot of event listeners to make your website interactive or maybe just for those flashy animations and forget to remove them when the user moves to some other page in your single page application. Now when the user moves back and forth between these pages, these listeners keep adding up.

Event listening: Suppose you create a large number of listening events in a web page for interaction or animation, and you forget to remove them when the user jumps to another page in a one-page application, memory leaks can occur. As the user jumps back and forth between these pages, these event listeners add up.

var element  = document.getElementById('button');
element.addEventListener('click', onClick)
Copy the code

Timer: When referring to objects in these closures, the garbage collector will never clean up the referenced objects until the closure itself is cleared.

setInterval(() => {
  // reference objects
}
// now forget to clear the interval.
// you just created a memory leak!
Copy the code

Deleted DOM nodes: Somewhat similar to global variable memory leaks, and very common. DOM nodes are stored in Object Graph Memory and DOM tree. An example can better illustrate the situation.

var terminator = document.getElementById('terminate');
var badElem = document.getElementById('toDelete');
terminator.addEventListener('click'.function()  {memory
  badElem.remove();
});
Copy the code

After clicking the button with ID =’terminate’, the toDelete node will be removed from the DOM Tree. However, since the object badElem is referenced in the event listener, it is assumed that the memory allocated by this object is still in use.

var terminator = document.getElementById('terminate');
terminator.addEventListener('click'.function()  {
  var badElem = document.getElementById('toDelete');
  badElem.remove();
});
Copy the code

The badElem variable is now defined as a local variable whose memory can be reclaimed by the garbage collector when the delete operation is complete.

The call stack

A stack is a data structure that follows LIFO (Last in, first out) to store and access data. For JavaScript engines, the stack is used to remember the location of the last command executed in a function.

function multiplyByTwo(x) {
  return x*2;
}
function calculate() {
  const sum = 4 + 2;
  return multiplyByTwo(sum);
}
calculate()
var hello = "some more code follows"
Copy the code
  1. First, the engine knows that there are two functions in the program.
  2. runcalculate()Function.
  3. Pops up on the call stackcalculateAnd compute the sum.
  4. runmultiplyByTwo()Function.
  5. Pop up on the call stackmultiplyByTwoFunction and perform the arithmetic operation x * 2.
  6. When the value is returned, it pops from the stackmultiplyByTwo()And then returncalculate()Function.
  7. incalculate()When the function returns, it pops off the stackcalculate, and continue executing the code.

Stack overflow

Stack overflow

Without popping the stack, the amount of continuous pushing depends on the size of the stack. If you continue to push the stack to the limit, the stack will overflow, and Chrome will report an error and generate a stack snapshot, also known as a stack frame.

Recursion: When a function calls itself, it is called recursion. Recursion can be useful when you want to reduce the time (time complexity) that an algorithm takes to execute, but other methods are complex to understand and implement.

In the following example, the return 1 statement is never executed, and the Lonely function keeps calling itself without returning, resulting in a stack overflow.

function lonely() {
 if (false) {
  return 1;  // the base case
 }
 lonely();   // the recursive call
}
Copy the code

Why is JavaScript single threaded?

Multiple threads means that you can execute multiple parts of a program independently at the same time. The easiest way to determine whether a language is single-threaded or multi-threaded is to look at how many call stacks it has. There is only one JS, so it is a single-threaded language.

Isn’t this a bottleneck, you might think? If I run multiple time-consuming operations, also known as blocking operations (such as HTTP requests), the program will have to wait for the response of each operation to complete before performing the next operation.

To solve this problem, we needed a way to execute tasks asynchronously to overcome the disadvantages of single threads, which led to event loops.

Event loop

For now, most of the above is covered by V8, but if you search the V8 code base for implementations of things like setTimeout or DOM, you probably won’t find anything. Because in addition to the runtime engine, JS contains Web API modules that browsers provide to extend JS functionality.

You can learn more about this in this video.

conclusion

There’s a lot of work to be done writing a programming language, and the way it’s implemented can change from year to year. I hope these two articles will help you become a better JS programmer and embrace the weird parts of JS. By now, you should get used to V8, event loops, call stacks, and so on.

Most people like me start learning JS by learning a new framework. I think we should now have some understanding of what’s going on inside the engine, which will help us write better code.