Dev.to WebDev 🛠 Dev 👁 1 📖 6 min read

Understanding the V8 Engine (The Brain Behind Node.js) — From First Principles

"I don't want to memorize what V8 does. I want to understand why it exists and how it actually works." If you've ever searched "How does Node.js work internally?", you've probably seen something like this: V8 parses

"I don't want to memorize what V8 does. I want to understand why it exists and how it actually works."

If you've ever searched "How does Node.js work internally?", you've probably seen something like this:

  • V8 parses JavaScript
  • V8 produces bytecode
  • V8 executes bytecode
  • V8 manages the call stack
  • V8 manages heap memory
  • V8 performs garbage collection

Cool...

But what does any of that actually mean?

This article explains V8 from first principles, without assuming any compiler knowledge. By the end, you'll understand why each of these steps exists instead of simply memorizing them.

Before We Start: Who Actually Executes JavaScript?

Many beginners believe Node.js executes JavaScript.

It doesn't.

Think of Node.js as a restaurant owner.

V8 is the chef.

You
 │
 ▼
Node.js
 │
 ▼
V8 Engine
 │
 ▼
CPU

Your JavaScript is given to the V8 Engine, and V8 is responsible for understanding and executing it.

The Real Problem

Imagine you hired a worker from another country.

You speak English.

The worker only understands Chinese.

You say:

console.log("Hello");

To you, this makes perfect sense.

To the worker, it's just strange symbols.

Exactly the same thing happens with your CPU.

The CPU doesn't understand JavaScript.

It understands only machine instructions like:

101010...
110100...
001010...

Somebody has to translate JavaScript into something the CPU understands.

That translator is V8.

Step 1 — Parsing JavaScript

Suppose we write:

let age = 25;

Open this file in Notepad.

You'll literally see:

l
e
t

a
g
e

=

2
5
;

To your computer, this is nothing more than plain text.

So the first thing V8 does is read and understand your code.

This process is called Parsing.

Think Like Your Brain

Suppose someone says:

John eats pizza.

Your brain instantly understands:

  • Person → John
  • Action → eats
  • Object → pizza

You don't analyze individual letters.

Your brain understands the structure.

V8 does exactly the same thing.

When it sees:

let age = 25;

it understands:

Variable Declaration

Variable Name → age

Assigned Value → 25

Notice something.

Nothing has been executed.

V8 is simply understanding what the code means.

Another Example

let total = price + tax;

You instantly know what should happen:

  1. Read price
  2. Read tax
  3. Add them
  4. Store the result inside total

V8 reaches exactly the same understanding.

Internally...

Instead of storing plain text, V8 creates a structured representation of your program called an Abstract Syntax Tree (AST).

For example:

a + b

becomes something conceptually like:

     +
    / \
   a   b

This structure is much easier for V8 to work with than raw text.

Why Doesn't V8 Execute the Code Immediately?

Understanding the code isn't enough.

Imagine telling a robot:

Cook rice.

You understand that instruction.

The robot doesn't.

The robot needs tiny steps.

Take pot

↓

Add water

↓

Add rice

↓

Turn stove on

Computers are exactly the same.

They need extremely small instructions.

Step 2 — Generating Bytecode

After parsing, V8 converts your code into Bytecode.

Think of bytecode as a simplified language that is much easier to execute.

Your code:

let x = 5;

Conceptually becomes something like:

Create variable

↓

Store value 5

↓

Name it x

These aren't real bytecode instructions.

They're simplified just to understand the idea.

The real bytecode is more detailed.

Why Bytecode?

A common question is:

Why not generate machine code immediately?

Good question.

Imagine you're reading a 500-page book.

Would you translate the entire book into another language before reading it?

Probably not.

You'd rather translate page by page.

Generating bytecode is much faster.

It lets your application start quickly.

Step 3 — Executing the Bytecode

Now V8 begins executing those instructions.

Suppose our code is:

let x = 5;
let y = 10;

let z = x + y;

Conceptually, V8 performs something like:

Create x

↓

Store 5

↓

Create y

↓

Store 10

↓

Read x

↓

Read y

↓

Add

↓

Store result in z

One instruction at a time.

This execution is initially performed by V8's Ignition Interpreter.

Wait... Isn't Interpreting Slow?

Yes.

Imagine this loop:

for (let i = 0; i < 100000000; i++) {

}

If V8 keeps interpreting every single iteration forever, it'll become slow.

So V8 watches your code.

When it notices that a function is executed thousands of times, it says:

"This code is important."

Now another component called TurboFan takes over.

TurboFan compiles that frequently executed ("hot") code into optimized machine code.

This is called Just-In-Time (JIT) Compilation.

Instead of interpreting repeatedly:

Interpreter

↓

Interpreter

↓

Interpreter

↓

Interpreter

V8 eventually does:

Machine Code

↓

CPU

This is significantly faster.

Step 4 — The Call Stack

Let's look at functions.

function eat() {
    cook();
}

function cook() {
    buyVegetables();
}

function buyVegetables() {
    console.log("Done");
}

eat();

How does V8 remember where it is?

It uses something called the Call Stack.

Initially:

(empty)

Call eat():

eat

Inside eat(), call cook():

cook

eat

Inside cook(), call buyVegetables():

buyVegetables

cook

eat

When buyVegetables() finishes:

cook

eat

Then:

eat

Finally:

(empty)

Notice the pattern.

The last function called is the first one to finish.

This is called LIFO (Last In, First Out).

What Happens If the Stack Never Ends?

Consider:

function hello() {
    hello();
}

hello();

The stack becomes:

hello()

hello()

hello()

hello()

hello()

...

Eventually V8 runs out of stack space and throws:

RangeError:
Maximum call stack size exceeded

This is called a Stack Overflow.

Step 5 — Heap Memory

Now let's talk about objects.

Suppose we create:

let person = {
    name: "Ram",
    age: 25
};

Objects require memory.

Imagine the heap as a giant warehouse.

Heap

+--------------------+
| name : Ram         |
| age : 25           |
+--------------------+

The variable itself doesn't contain the object.

Instead, it stores a reference (an address) pointing to where the object lives.

Conceptually:

person

↓

Object in Heap

Think of it like your phone contacts.

Your contact doesn't store your friend's house.

It stores the address of the house.

Why Do Objects Live in the Heap?

Because objects can grow.

person.city = "Kathmandu";
person.country = "Nepal";
person.phone = "9800000000";

The heap allows memory to be allocated dynamically.

Step 6 — Garbage Collection

Suppose we create:

let person = {
    name: "Ram"
};

Memory looks like:

person

↓

Object

Now we write:

person = null;

The variable no longer points to the object.

person

↓

null

Object (still exists)

The object is still sitting in memory.

But nothing can reach it anymore.

Later, V8 checks memory.

It notices:

"Nobody can ever use this object again."

So it removes it and frees the memory.

This process is called Garbage Collection.

How Does V8 Know an Object Is Unused?

V8 starts from roots, such as:

  • Global variables
  • Variables inside currently running functions
  • Closures that are still reachable

From those roots, it follows every reference.

If an object can still be reached, it stays.

If nothing points to it anymore, it becomes garbage.

A simplified view:

Global

↓

person

↓

address

↓

city

Everything connected to the roots stays alive.

But:

Old Object

(no references)

Since nothing can reach it anymore, V8 deletes it.

This is the basic idea behind the Mark-and-Sweep Garbage Collection algorithm.

Putting Everything Together

When you execute:

function greet(name) {
    return `Hello ${name}`;
}

console.log(greet("Manoj"));

The journey looks like this:

JavaScript Source Code
          │
          ▼
       Parsing
          │
          ▼
Abstract Syntax Tree (AST)
          │
          ▼
Generate Bytecode
          │
          ▼
Ignition Interpreter Executes Bytecode
          │
          ▼
Manage Call Stack
          │
          ▼
Allocate Objects in Heap
          │
          ▼
TurboFan Optimizes Frequently Used Code
          │
          ▼
Machine Code
          │
          ▼
CPU Executes Instructions
          │
          ▼
Garbage Collector Frees Unused Memory

One Important Clarification

A lot of developers think V8 is responsible for everything inside Node.js.

It isn't.

V8 is responsible for:

  • Parsing JavaScript
  • Creating the AST
  • Generating bytecode
  • Executing JavaScript
  • Managing the call stack
  • Managing heap memory
  • Garbage collection
  • Optimizing hot code with JIT compilation

However, V8 does not handle:

  • File system operations (fs)
  • Network I/O (http, https, net)
  • Timers (setTimeout, setInterval)
  • The Event Loop
  • Thread Pool

These are handled by Node.js and its underlying library libuv.

A simplified architecture looks like this:

                 Your JavaScript
                        │
                        ▼
                 Node.js Runtime
         ┌─────────────────────────┐
         │                         │
         ▼                         ▼
     V8 Engine                  libuv
(JavaScript Execution)      (I/O & Event Loop)
         │                         │
         └──────────────┬──────────┘
                        ▼
                 Operating System
                        ▼
                       CPU

Understanding this separation is one of the biggest milestones in learning how Node.js actually works.

Final Thoughts

When I first learned about V8, I tried to memorize terms like AST, bytecode, heap, and garbage collection.

None of it made sense.

Everything clicked only after I stopped asking:

"What does V8 do?"

and started asking:

"Why does V8 need to do this?"

Once you realize that JavaScript is just text and the CPU understands only machine instructions, every piece of V8 starts to make logical sense.

And that's the goal not rote learning, but understanding the why behind the magic.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.