Back to all articles
Technological Overview

JavaScript Under the Hood: The V8 Engine, Execution Context, and Memory Architecture

Demystifying runtime internals for senior engineers: how the V8 engine parses ASTs, compiles hot code via TurboFan, and manages system memory from Call Stack frames to the dynamic Heap.

E

Elgun Mammadli

Front End Developer

Oct 7, 2026
6 min read
JavaScript Under the Hood: The V8 Engine, Execution Context, and Memory Architecture

Most developers start their JavaScript journey with console.log("Hello World"), variable declarations, or simple functions. However, mastering the language at a senior engineering level demands looking past the surface syntax: What does our code actually mean to the machine, and how does the engine execute it on the physical hardware?

In this deep dive, we explore JavaScript's internal mechanics: the V8 engine optimization pipeline, the lifecycle of an Execution Context, and the low-level reality of memory management.


1. Engine Internals: From Source Code to Machine Instructions

JavaScript is no longer a purely interpreted language; modern implementations leverage JIT (Just-In-Time) Compilation. The most prominent runtime engine is Google's V8, which powers Google Chrome, Node.js, and Deno.

When source code enters the engine, it navigates the following multi-stage pipeline:

pipeline-architecture.txt
Source Code 
    │
    ▼
[ Parser / Scanner ]  ──►  Token stream & AST (Abstract Syntax Tree)
    │
    ▼
[ Ignition ] (Interpreter) ──►  Bytecode  ──► Executes
    │
    ├─► Profiler (Type Feedback & "Hot Code" analysis)
    │
    ▼
[ TurboFan ] (Optimizing Compiler) ──► Highly Optimized Machine Code

Parser and the AST (Abstract Syntax Tree)

The engine begins with lexical analysis: scanning the raw characters into discrete tokens. Next, the parser converts these tokens into an AST—a hierarchical, tree-structured syntactic representation of the program.

Ignition (The Interpreter)

V8's interpreter, Ignition, consumes the AST and translates it into compact, platform-independent Bytecode. Bytecode runs immediately without waiting for heavy compilation steps, delivering instant startup times.

TurboFan and the Profiler (JIT Optimization)

As the bytecode executes, an internal Profiler monitors runtime behavior to detect frequently invoked functions—labeled as "Hot Code". It also collects runtime Type Feedback.

When a function is consistently invoked with stable types, V8's optimizing compiler, TurboFan, translates that bytecode directly into high-performance Machine Code tailored for the host CPU.

The Deoptimization Trap

JavaScript is dynamically typed. If TurboFan aggressively optimizes a function for numbers (number), and the code suddenly feeds it a string (string):

deoptimization.js
function add(a, b) {
  return a + b;
}

// TurboFan optimizes this function for 'number' inputs (Inline Caching)
for (let i = 0; i < 10000; i++) {
  add(5, 10);
}

// Unexpected type mutation:
add("5", "10"); // Deoptimization triggered!

The engine discards the optimized machine instructions (a bailout) and drops back to slower bytecode interpretation. Writing monomorphic code (retaining consistent object shapes and variable types) is paramount for performance.


2. What Is an Execution Context?

From the moment code executes, JavaScript evaluates everything inside dedicated environments called Execution Contexts (EC). Simply put, an Execution Context is the conceptual wrapper containing the code currently being evaluated.

There are two primary variants:

  1. Global Execution Context (GEC): Instantiated automatically when the script loads. Only one GEC exists, establishing the global object (window in browsers, global in Node.js).
  2. Function Execution Context (FEC): Created on demand whenever a function is invoked.

Every execution context progresses through two distinct phases:

Phase 1: Creation Phase

Before executing a single line of code, the engine scans the scope and allocates memory:

  • The global object (window or global) is bound.
  • The this reference is resolved for the current scope.
  • The Lexical Environment and Variable Environment structures are established.
  • Memory space is reserved for identifiers and functions (this is the physical foundation of Hoisting).

Phase 2: Execution Phase

The engine steps sequentially through the code, binds concrete values to variables, and executes function invocations.


3. Hoisting and the Temporal Dead Zone (TDZ)

Hoisting is not physical code movement; it is an artifact of the Creation Phase allocating variable slots before runtime execution begins.

The distinction lies in how different declarations are initialized during this step:

hoisting-tdz.js
console.log(a); // undefined
console.log(b); // ReferenceError: Cannot access 'b' before initialization

var a = 10;
let b = 20;
  • The var keyword: Memory is allocated during the creation phase and immediately initialized to undefined. Accessing it prior to assignment returns undefined without throwing an error.
  • The let and const keywords: Memory is registered during the creation phase, but the identifier remains uninitialized. The window between entry into scope and the physical declaration statement is the Temporal Dead Zone (TDZ). Accessing an uninitialized identifier triggers a ReferenceError.

4. The Call Stack and Memory Architecture: Stack vs. Heap

JavaScript is a single-threaded runtime with a single sequential call stack. Execution flow is managed by the Call Stack, while non-primitive data storage is coordinated within the Memory Heap.

Property Call Stack Memory Heap
Role Execution flow, primitives, and memory references (pointers) Complex objects, arrays, and functions
Structure Strict LIFO (Last In, First Out) ordering Unordered, dynamically allocated memory space
Access Speed Extremely fast direct memory access Marginally slower due to pointer dereferencing
memory-allocation.js
const id = 101;                                 // Stored directly on the Stack
const user = { name: "Elgun", role: "Dev" }; // Object allocated on the Heap, pointer on the Stack

The stack holds only the memory address pointer (0x0012FA) referencing the heap. When an object is assigned to another variable, only this pointer is copied, not the underlying object data:

reference-mutation.js
const userA = { name: "Ali" };
const userB = userA; 

userB.name = "Murad";
console.log(userA.name); // "Murad" (Both pointers reference the identical heap address)

The Call Stack and Stack Overflow

Each function invocation pushes a new Stack Frame onto the Call Stack. Once execution completes, the frame is popped. If recursive calls occur without an exit condition, stack memory is exhausted, throwing Maximum call stack size exceeded.


5. Garbage Collection: Mark-and-Sweep

Dynamically allocated heap memory cannot persist indefinitely. The engine's Garbage Collector (GC) periodically identifies and reclaims unreferenced memory.

Modern engines rely on the Mark-and-Sweep algorithm:

  1. Roots: Global variables and local variables in currently active stack frames form the baseline roots.
  2. Mark: The GC traverses references beginning from the roots, tagging all encountered objects as "reachable". It then follows outbound references recursively.
  3. Sweep: Any object on the heap lacking an active reference path from the root set is marked unreachable, and its allocated memory is reclaimed.

The Threat of Memory Leaks

When an object is no longer functional but remains anchored by an uncleaned event listener (addEventListener), an unintended global identifier, or an orphaned closure, the GC cannot sweep it. Over time, memory consumption climbs, introducing latency and browser tab crashes.


Summary

  • The V8 Engine does not merely interpret code; it transforms AST → Bytecode (Ignition) → Optimized Machine Code (TurboFan).
  • Execution Contexts govern variable scopes across Creation and Execution phases. Hoisting and the TDZ are direct outcomes of the Creation phase.
  • Stack vs. Heap: Primitives and memory references live on the fast-access Stack, while complex objects reside in the dynamic Heap.
  • Garbage Collection hinges on reachability; severed reference chains allow the engine to reclaim memory.

With the engine fundamentals established, the next architectural step is analyzing how these structures interact through the Scope Chain, Lexical Environment, and Closures.

Share this article

JavaScript Under the Hood: The V8 Engine, Execution Context, and Memory Architecture