Hidden classes in JavaScript and Inline Caching

Alex Navasardyan
3 min readNov 28, 2014

--

Knowing how internals work is always a good. Pretty much for everything. Cars, trains, computers, you name it. It gives you an insight on what happens under the hood. You also act/react differently based on this knowledge.

As you might have guessed, it’s also true for web development. Knowledge of CSS transitions allows you to achieve better performance and not to use JavaScript in most cases. Knowledge of V8 internals allows you to write more performant JavaScript code. So let’s talk about V8 a little.

A little about V8

V8 is a JavaScript engine built by Google. Firefox built SpiderMonkey, Opera built Carakan and Microsoft built Chakra. One very important difference between V8 and other JavaScript engines is that V8 doesn’t generate any intermediate code. It compiles JavaScript to machine code at execution.

Hidden Classes

It’s rather hard to optimize a dynamically typed, prototype-based language, such as JavaScript. Objects can change their type during runtime and it happens implicitly. To track types of JavaScript object and variables, V8 introduced the concept of hidden classes. During runtime V8 creates hidden classes that get attached to each and every object to track its shape/layout. That allows to optimize the property access time.

Consider the example:

function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
var ironMan = new Person(‘Tony’, ‘Stark’);
var captainAmerica = new Person(‘Steve’, ‘Rogers’);
captainAmerica.age = 87;

When `Person` function gets invoked to initialize `ironMan` object, a hidden class (let’s call it `C0`) gets created along with invocation. `C0` is an empty class because `Person` doesn’t have any properties yet. Next, `this.firstName` gets assigned and hidden class `C1` gets created. `C1` is based on `C0` with `firstName` property. Next, `this.lastName` gets assigned and guess what? Hidden class `C2` gets created. `C2` based on `C1` with `lastName` property.

When `Person` gets invoked to initialize `captainAmerica`, the hidden class chain (`C0` -> `C1` -> `C2`) is going to reused and `captainAmerica` and `ironMan` are going to have the same hidden class. But as soon as the shape(layout) of the object changes, it gets a new hidden class. By assigning `87` to the `age` property, we created a new hidden class `C2` that is going to be based on `C2` with `age` property on it. It’s very important to maintain the shape of your objects so V8 can optimize the code more efficiently.

Inline Cache

In order to optimize property access, V8 uses an old technique called Inline Caching. You can think of Inline Cache as a fast path (shortcut) to the value/property.

Consider the example:

this.puppies[0]
push [ebp+0x8]
mov eax,[ebp+0xc]
mov edx, eax
mov ecx, 0x60b55dd1
call LoadIC_Initialize ;; this.puppies

When you try to access `this.puppies`, V8 is going do a call out to Inline Cache. And as your code keeps running and/or getting “hotter”, V8 will make an optimistic but usually valid assumption that the value is going to stay the same. Therefore, the call out to Inline Cache can be replaced with just an address in the memory, like so:

push [ebp+0x8]
mov eax,[ebp+0xc]
mov edx, eax
mov ecx, 0x60b55dd1
call 0x311286e0 ;; this.puppies

It results in a huge (~20x) speed improvement.

Deoptimizations and Bailouts

V8 is generally pretty optimistic about your code and tries to speed it up as much as it can. But sometimes the assumptions that it makes are not valid (a hidden class wasn’t the one expected). In this case, V8 will replace Inline Cache fast path code with full non-optimized code.

Bailouts happen when V8 can’t optimize some parts of your code because it doesn’t support some language features. V8 won’t optimize functions with `try {} catch()` or with `with {}` statements in them.

--

--