Inside the V8 Engine: How JavaScript Really Works
Deep dive into V8 engine architecture, JIT compilation, garbage collection, and optimization techniques.

V8 is a Just-In-Time (JIT) compiling engine that does not interpret JavaScript line by line but rather compiles it into machine code for faster execution. Let's explore the key stages of V8's operation and its optimization mechanisms.
1. How Does V8 Execute JavaScript Code?
When JavaScript code is passed into V8, it goes through several stages:
1️⃣ Parsing
V8 first analyzes (parses) the JavaScript code:
- Lexical analysis (Tokenization) – breaks code into tokens (e.g.,
var
,function
,if
). - AST generation (Abstract Syntax Tree) – creates a tree representation of the code.
Example AST for let x = 10;
:
{
"type": "VariableDeclaration",
"declarations": [
{
"id": { "type": "Identifier", "name": "x" },
"init": { "type": "Literal", "value": 10 }
}
],
"kind": "let"
}
2️⃣ Interpretation (Ignition Interpreter)
After parsing, V8 uses Ignition, an interpreter that translates AST into bytecode.
Bytecode example for let x = 10;
:
LdaSmi 10
Star r0
LdaSmi 10
– load number10
into a register.Star r0
– store the value in registerr0
.
This bytecode is faster than AST but still slow because it's not yet optimized.
3️⃣ Compilation into Machine Code (TurboFan)
To make execution faster, V8 analyzes the bytecode and JIT-compiles it into machine code using TurboFan.
Example of JavaScript code transformed into machine instructions:
mov eax, 10 ; load 10 into the EAX register
mov [rbp-4], eax ; store it in a local variable
2. Code Optimization in V8
V8 employs several strategies to speed up execution:
1️⃣ Inline Caching (IC)
- Optimizes object property access.
- If V8 frequently accesses
obj.name
, it remembers its location and speeds up retrieval.
Example:
function getName(user) {
return user.name
}
On the first call to getName({ name: "Denis" })
, V8 analyzes the object type and creates a fast access path for name
.
2️⃣ Hidden Classes
- JavaScript objects are dynamic: properties can be added or removed.
- V8 creates hidden classes to speed up property access.
Example:
let user = {}
user.name = 'Denis' // V8 creates hidden class C1
user.age = 30 // A new class C2 is created (due to structure change)
To avoid changing classes, it's better to declare all properties upfront:
let user = { name: 'Denis', age: 30 }
3️⃣ Escape Analysis (Memory Allocation Optimization)
- If V8 detects that an object does not escape a function's scope, it allocates it on the stack instead of the heap (heap allocation).
- This reduces Garbage Collector workload.
Example:
function createUser(name) {
return { name } // May be allocated on the stack
}
4️⃣ Optimistic Compilation (Decompilation on Type Change)
V8 initially assumes an optimal execution path.
- If an error is detected (e.g., a data type changes), the code falls back to the Ignition interpreter.
Example:
function add(a, b) {
return a + b
}
add(5, 10) // TurboFan compiles as int32
add('5', 10) // Code falls back to interpreter
5️⃣ Garbage Collector (Orinoco)
V8 uses a modern garbage collector:
- Minor GC (Young Generation) – cleans up short-lived objects.
- Major GC (Old Generation) – removes long-lived objects.
Optimization tips for GC:
✔ Use let
and const
instead of var
– var
creates global variables.
✔ Avoid global variables – they stay in memory longer.
✔ Use Map
instead of {}
for large collections – Map
is cleaned up faster.
3. Conclusion
✅ V8 is one of the fastest JavaScript engines thanks to JIT compilation.
✅ Ignition + TurboFan enable a mix of interpretation and compilation.
✅ V8 uses Inline Caching, Hidden Classes, and Escape Analysis for speed optimization.
✅ The Orinoco garbage collector minimizes latency.