raw
current commit
261004.7 · deps: update V8 to 15.2.124.38

Field Representations and Elements Kinds

This document explains how V8 optimizes object properties and array elements by specializing their storage representation based on the types of values they contain.

Overview

JavaScript is dynamically typed, but V8 attempts to treat objects as if they had static types to improve performance. Two key mechanisms for this are Field Representations (for named properties) and Elements Kinds (for indexed properties/array elements).


Field Representations

When properties are stored in the Descriptor Array of a Map, V8 tracks the Representation of each data property. This allows V8 to avoid boxing numbers or storing full pointers when not necessary.

Representation Types

Defined in src/objects/property-details.h, the main representations are:

PropertyDetails

Every property has an associated PropertyDetails value (a 32-bit integer) that packs:

Deep Dive: PropertyDetails Bit Layout

V8 packs this information tightly into a 32-bit integer. The layout differs between fast mode (using descriptor arrays) and slow mode (dictionary properties).

For Fast Mode Properties:

For Dictionary Mode Properties:

This bit-packing allows V8 to pass property metadata efficiently and perform quick checks using bitmasks.

Generalization vs. Transitions

It is important to distinguish between Generalization (representation changes) and Transitions (adding fields):

If a property is initialized as a Smi and later assigned a Double, V8 will generalize the representation. This may require Map Deprecation (marking the old map as invalid for new objects) and creating a new map with the generalized representation. Objects with the old deprecated map are not updated immediately; instead, they are lazily migrated to the new map when they are next accessed or mutated. V8 cannot generalize “backwards” to a more specific representation.

note

A field representation change (generalization) is one of the ways to trigger a Lazy Deoptimization in optimized code that relied on the more specific representation. See Deoptimization for details.

Some representation changes can be done in-place without deprecating the map. Specifically, generalizing from Smi, Double, or HeapObject to Tagged can be done in-place. However, changing representation from Smi to Double requires deprecation because doubles might require a box allocation (e.g., HeapNumber).

Slack Tracking

When V8 allocates a new object instance, it often allocates more space than currently needed for properties (in-object slack).


Property Locations: Fields vs. Descriptors

In addition to representation, V8 tracks where a property’s value is stored, defined by PropertyLocation in src/objects/property-details.h:

By combining PropertyKind (Data vs. Accessor) and PropertyLocation (Field vs. Descriptor), V8 can represent:


Elements Kinds

For indexed properties (arrays), V8 uses Elements Kinds to specialize the backing store (FixedArray or FixedDoubleArray) and optimize operations like map, reduce, and forEach.

Common Elements Kinds

Defined in src/objects/elements-kind.h, the most common “fast” kinds are:

Other Elements Kinds

For completeness, V8 also defines elements kinds for special cases:

Dictionary Mode Elements

When an array becomes very sparse or has a large number of elements, V8 may switch from a flat backing store (FixedArray or FixedDoubleArray) to a dictionary-based representation (NumberDictionary).

Packed vs. Holey

Elements Kind Transitions

Elements kinds only transition from more specific to more general through a lattice. Once transitioned, they rarely go back:

note

While conceptually elements kinds form a lattice of generalization, V8’s implementation in the transition tree linearizes transitions for fast elements kinds to keep the tree simple and avoid path explosion. The linear sequence used is: PACKED_SMI -> HOLEY_SMI -> PACKED_DOUBLE -> HOLEY_DOUBLE -> PACKED_ELEMENTS -> HOLEY_ELEMENTS This means V8 may create intermediate transition maps (e.g., creating a HOLEY_SMI map when transitioning from PACKED_SMI to PACKED_DOUBLE) even if they are not strictly needed for the final representation.

Examples of Transitions:

const array = [1, 2, 3]; // PACKED_SMI_ELEMENTS
array.push(4.56);        // Transitions to PACKED_DOUBLE_ELEMENTS
array.push('x');         // Transitions to PACKED_ELEMENTS
array[9] = 1;            // Transitions to HOLEY_ELEMENTS (indices 5-8 are holes)

File Structure