Arrays Deep Dive

V8 Packed/Holey and SMI/Double/Object Kinds Determine Real Array Performance

Arrays Deep Dive

JavaScript arrays are not homogeneous blocks of memory — V8 uses several internal representations whose performance characteristics differ dramatically from each other.

5 min read Level 2/5 #dsa#arrays#v8-internals
What you'll learn
  • Distinguish V8 packed vs. holey arrays and SMI/double/object element kinds
  • Choose TypedArrays for numeric-heavy workloads that need predictable performance
  • Avoid common patterns that silently degrade an array from a fast to a slow kind

A JavaScript array feels like a simple ordered list, but V8 — the engine behind Node.js and Chrome — stores arrays in several very different ways depending on what you put into them. Getting arrays wrong can make tight loops 3–10x slower than necessary.

Element Kinds

V8 tracks the “element kind” of every array. The kinds form a one-way lattice: once an array is demoted to a slower kind it never goes back.

KindTriggerSpeed
PACKED_SMI_ELEMENTSAll values are small integers, no holesFastest
PACKED_DOUBLE_ELEMENTSAll values are doubles, no holesFast
PACKED_ELEMENTSMixed values, no holesModerate
HOLEY_SMI_ELEMENTSSmall integers but with holesSlower
HOLEY_DOUBLE_ELEMENTSDoubles with holesSlower
HOLEY_ELEMENTSMixed values with holesSlowest

“Holes” are created by delete arr[i], by allocating new Array(n) (preallocated but unfilled), or by writing past the current length.

// Starts as PACKED_SMI_ELEMENTS
const a = [1, 2, 3];

// Demoted to PACKED_DOUBLE_ELEMENTS
a.push(1.5);

// Demoted to PACKED_ELEMENTS (objects)
a.push("hello");

// Now holey — never recovers
delete a[0];

Packed vs. Holey

A packed array has no gaps. V8 can iterate it without checking whether each index is defined, which lets it skip a prototype-chain lookup per element. A holey array must check every index, falling back to Array.prototype if the slot is empty — that per-element lookup kills throughput in tight loops.

Practical rules: always fill arrays before use, prefer Array.from or .fill over new Array(n), and avoid delete on array indices (use splice instead).

TypedArrays

When all values are numbers, TypedArray is the right tool. It uses a true contiguous block of memory with a fixed element type.

// 32-bit integer array — 4 bytes per element, contiguous
const ints = new Int32Array(1_000_000);

// 64-bit floating-point — same as C double
const floats = new Float64Array([1.1, 2.2, 3.3]);

// Shared backing buffer across two views
const buf = new ArrayBuffer(8);
const i32 = new Int32Array(buf);
const f32 = new Float32Array(buf);
i32[0] = 42;
console.log(f32[0]); // same bytes, different interpretation
Array typeElement sizeBacked byBest for
Regular ArrayVariableHidden class + propertiesMixed data, sparse lists
Int32Array4 bytesArrayBufferInteger math, image pixels
Float64Array8 bytesArrayBufferScientific / financial data
Uint8Array1 byteArrayBufferBinary protocols, crypto

When to Use Which

Use a regular array when elements are mixed type or the size is unknown. Use a TypedArray when you have a large, homogeneous numeric dataset — the difference in a tight loop can be measured in microseconds per element.

Up Next

Now that you understand what arrays really are internally, see the Big-O cost of the methods you call on them every day.

Array Method Complexity →