How V8 Runs JavaScript

What's a JavaScript Engine?

APIs

document.createElement DOM Standard
setTimeout HTML Standard
console.log Console Standard

=> Built into browser

JavaScript Engines

Chrome V8
Firefox SpiderMonkey
Edge Chakra
Safari JavaScriptCore (Nitro)
Node V8
Opera V8
Electron V8
Node-ChakraCore ChakraCore

Goals & Trade-offs

Fast Execution

Quick Startup

Low Memory Consumption

Adaptive Optimization

Compiler

Interpreter

Syntax Tree

Bytecode

Machine Code

Quick Startup, Low Memory

Fast Execution

Execution

Memory

Parsing

Object Types

var hi = "Hello"

Parsing

function sayHi(name){
    var message = "Hi " + name + "!"
    console.log(message)
}

sayHi("Sparkle")
$ node --trace_parse test.js 
$ node --trace_parse test.js

[parsing script: native harmony-regexp-exec.js - took 0.165 ms]
[parsing function: ImportNow - took 0.020 ms]
[parsing function: OverrideFunction - took 0.028 ms]
[parsing function: SetFunctionName - took 0.022 ms]
[parsing script: native harmony-species.js - took 0.052 ms]
[parsing function: get __proto__ - took 0.012 ms]
[parsing function: InstallGetter - took 0.020 ms]
[parsing script: native harmony-unicode-regexps.js - took 0.043 ms]
[parsing function: Import - took 0.008 ms]
[parsing script: native promise-extra.js - took 0.023 ms]
[parsing function: InstallFunctions - took 0.032 ms]
[parsing function: PostExperimentals - took 0.027 ms]
[parsing function:  - took 0.007 ms]
[parsing function:  - took 0.008 ms]
[parsing function:  - took 0.005 ms]
[parsing function: b.CreateDoubleResultArray - took 0.008 ms]
[parsing function: Float64Array - took 0.040 ms]
[parsing function: Float64ArrayConstructByLength - took 0.024 ms]
[parsing function: ToPositiveInteger - took 0.015 ms]
[parsing script: bootstrap_node.js - took 0.611 ms]
[parsing function: some - took 0.036 ms]
[parsing function: InnerArraySome - took 0.034 ms]
[parsing function:  - took 0.010 ms]
[parsing function: match - took 0.050 ms]
[parsing function: [Symbol.match] - took 0.050 ms]
[parsing function: get global - took 0.040 ms]
[parsing function: RegExpSubclassExec - took 0.034 ms]
[parsing function: exec - took 0.059 ms]
[parsing function: startup - took 0.160 ms]
[parsing function: NativeModule.require - took 0.028 ms]
[parsing function: NativeModule.getCached - took 0.008 ms]
[parsing function: NativeModule.exists - took 0.006 ms]
[parsing function: NativeModule - took 0.013 ms]
[parsing function: NativeModule.cache - took 0.008 ms]
[parsing function: NativeModule.compile - took 0.022 ms]
[parsing function: NativeModule.getSource - took 0.007 ms]
[parsing function: NativeModule.wrap - took 0.008 ms]
[parsing function: runInThisContext - took 0.011 ms]
[parsing script: events.js - took 0.428 ms]
[parsing function: defineProperty - took 0.036 ms]
[parsing function: setPrototypeOf - took 0.025 ms]
[parsing function: EventEmitter - took 0.009 ms]
[parsing function: EventEmitter.init - took 0.025 ms]
[parsing function: getPrototypeOf - took 0.041 ms]
[parsing function: setupProcessObject - took 0.023 ms]
[parsing function: setupProcessFatal - took 0.033 ms]
[parsing function: setupGlobalVariables - took 0.038 ms]
[parsing script: util.js - took 1.090 ms]
[parsing script: buffer.js - took 1.063 ms]
[parsing script: internal/util.js - took 0.172 ms]
[parsing function: createPool - took 0.013 ms]
[parsing function: createUnsafeBuffer - took 0.013 ms]
[parsing function: FastBuffer - took 0.009 ms]
[parsing function: Uint8Array - took 0.048 ms]
[parsing function: Uint8ArrayConstructByLength - took 0.035 ms]
[parsing function: exports.deprecate - took 0.011 ms]
[parsing function: exports._deprecate - took 0.031 ms]
[parsing function: forEach - took 0.032 ms]
[parsing function: InnerArrayForEach - took 0.044 ms]
[parsing function:  - took 0.038 ms]
[parsing function: setupGlobalTimeouts - took 0.019 ms]
[parsing script: timers.js - took 0.463 ms]
[parsing script: internal/linkedlist.js - took 0.056 ms]
[parsing script: assert.js - took 0.286 ms]
[parsing function: exports.inherits - took 0.022 ms]
[parsing function: exports.debuglog - took 0.053 ms]
[parsing function: toUpperCase - took 0.019 ms]
[parsing function: RegExp - took 0.032 ms]
[parsing function: IsRegExp - took 0.014 ms]
[parsing function: RegExpInitialize - took 0.014 ms]
[parsing function: test - took 0.015 ms]
[parsing function: ImmediateList - took 0.019 ms]
[parsing function: setupGlobalConsole - took 0.038 ms]
[parsing script: internal/process.js - took 0.217 ms]
[parsing function: setup_hrtime - took 0.042 ms]
[parsing function: Uint32Array - took 0.056 ms]
[parsing function: Uint32ArrayConstructByLength - took 0.042 ms]
[parsing function: setup_cpuUsage - took 0.056 ms]
[parsing function: setupConfig - took 0.052 ms]
[parsing function: split - took 0.048 ms]
[parsing function: join - took 0.021 ms]
[parsing function: InnerArrayJoin - took 0.020 ms]
[parsing function: Join - took 0.018 ms]
[parsing function: StackHas - took 0.015 ms]
[parsing function: StackPush - took 0.008 ms]
[parsing function: DoJoin - took 0.057 ms]
[parsing function: UseSparseVariant - took 0.018 ms]
[parsing function: StackPop - took 0.009 ms]
[parsing function: replace - took 0.047 ms]
[parsing function: [Symbol.replace] - took 0.096 ms]
[parsing function: get unicode - took 0.022 ms]
[parsing function: [Symbol.replace] - took 0.045 ms]
[parsing function: parse - took 0.021 ms]
[parsing function: InternalizeJSONProperty - took 0.042 ms]
[parsing function:  - took 0.012 ms]
[parsing function: CreateDataProperty - took 0.015 ms]
[parsing script: internal/process/warning.js - took 0.084 ms]
[parsing function: setupProcessWarnings - took 0.057 ms]
[parsing function: addListener - took 0.010 ms]
[parsing function: _addListener - took 0.058 ms]
[parsing script: internal/process/next_tick.js - took 0.133 ms]
[parsing function: setupNextTick - took 0.115 ms]
[parsing script: internal/process/promises.js - took 0.096 ms]
[parsing function: WeakMap - took 0.038 ms]
[parsing function: setupPromises - took 0.078 ms]
[parsing script: internal/process/stdio.js - took 0.141 ms]
[parsing function: setupStdio - took 0.096 ms]
[parsing function: setupKillAndExit - took 0.044 ms]
[parsing function: setupSignalHandlers - took 0.046 ms]
[parsing function: emit - took 0.072 ms]
[parsing function: emitTwo - took 0.023 ms]
[parsing function:  - took 0.031 ms]
[parsing function: isSignal - took 0.010 ms]
[parsing function: lazyConstants - took 0.010 ms]
[parsing function: setupChannel - took 0.023 ms]
[parsing function: setupRawDebug - took 0.017 ms]
[parsing script: path.js - took 2.163 ms]
[parsing function: resolve - took 0.045 ms]
[parsing function: assertPath - took 0.012 ms]
[parsing function: charCodeAt - took 0.023 ms]
[parsing function: normalizeStringPosix - took 0.066 ms]
[parsing function: slice - took 0.037 ms]
[parsing script: module.js - took 0.585 ms]
[parsing script: internal/module.js - took 0.088 ms]
[parsing script: vm.js - took 0.134 ms]
[parsing script: fs.js - took 2.119 ms]
[parsing function: Script.runInThisContext - took 0.018 ms]
[parsing script: stream.js - took 0.105 ms]
[parsing script: _stream_readable.js - took 0.747 ms]
[parsing script: internal/streams/BufferList.js - took 0.082 ms]
[parsing script: _stream_writable.js - took 0.416 ms]
[parsing script: _stream_duplex.js - took 0.061 ms]
[parsing script: _stream_transform.js - took 0.136 ms]
[parsing script: _stream_passthrough.js - took 0.042 ms]
[parsing function:  - took 0.015 ms]
[parsing function: Map - took 0.039 ms]
[parsing function: Module._initPaths - took 0.039 ms]
[parsing function: preloadModules - took 0.012 ms]
[parsing function: run - took 0.026 ms]
[parsing function: Module.runMain - took 0.011 ms]
[parsing function: Module._load - took 0.040 ms]
[parsing function: Module._resolveFilename - took 0.035 ms]
[parsing function: NativeModule.nonInternalExists - took 0.025 ms]
[parsing function: Module._resolveLookupPaths - took 0.099 ms]
[parsing function: debugs.(anonymous function) - took 0.004 ms]
[parsing function: Module._findPath - took 0.102 ms]
[parsing function: isAbsolute - took 0.022 ms]
[parsing function: stringify - took 0.100 ms]
[parsing function: stat - took 0.030 ms]
[parsing function: _makeLong - took 0.006 ms]
[parsing function: toRealPath - took 0.021 ms]
[parsing function: realpathSync - took 0.131 ms]
[parsing function: nullCheck - took 0.030 ms]
[parsing function: indexOf - took 0.033 ms]
[parsing function: toString - took 0.013 ms]
[parsing function: get - took 0.037 ms]
[parsing function: GetExistingHash - took 0.023 ms]
[parsing function: MapFindEntry - took 0.038 ms]
[parsing function: HashToEntry - took 0.010 ms]
[parsing function: start - took 0.019 ms]
[parsing function: fs.lstatSync - took 0.011 ms]
[parsing function: fs.Stats - took 0.039 ms]
[parsing function: fs.Stats.isSymbolicLink - took 0.018 ms]
[parsing function: fs.Stats._checkModeProperty - took 0.008 ms]
[parsing function: set - took 0.065 ms]
[parsing function: GetHash - took 0.017 ms]
[parsing function: encodeRealpathResult - took 0.030 ms]
[parsing function: Module - took 0.026 ms]
[parsing function: tryModuleLoad - took 0.026 ms]
[parsing function: Module.load - took 0.031 ms]
[parsing function: ok - took 0.019 ms]
[parsing function: dirname - took 0.030 ms]
[parsing function: Module._nodeModulePaths - took 0.055 ms]
[parsing function: extname - took 0.044 ms]
[parsing function: Module._extensions..js - took 0.013 ms]
[parsing function: fs.readFileSync - took 0.062 ms]
[parsing function: assertEncoding - took 0.012 ms]
[parsing function: Buffer.isEncoding - took 0.009 ms]
[parsing function: normalizeEncoding - took 0.025 ms]
[parsing function: isFd - took 0.007 ms]
[parsing function: fs.openSync - took 0.015 ms]
[parsing function: modeNum - took 0.070 ms]
[parsing function: stringToFlags - took 0.054 ms]
[parsing function: tryStatSync - took 0.022 ms]
[parsing function: fs.fstatSync - took 0.007 ms]
[parsing function: fs.Stats.isFile - took 0.007 ms]
[parsing function: tryCreateBuffer - took 0.027 ms]
[parsing function: Buffer.allocUnsafe - took 0.007 ms]

[parsing function: assertSize - took 0.023 ms]
[parsing function: isNaN - took 0.024 ms]
[parsing function: get buffer - took 0.013 ms]
[parsing function: get byteOffset - took 0.009 ms]
[parsing function: Uint8ArrayConstructByArrayBuffer - took 0.069 ms]
[parsing function: alignPool - took 0.011 ms]
[parsing function: tryReadSync - took 0.019 ms]
[parsing function: fs.readSync - took 0.051 ms]
[parsing function: fs.closeSync - took 0.008 ms]
[parsing function: Buffer.toString - took 0.017 ms]
[parsing function: slowToString - took 0.054 ms]
[parsing function: stripBOM - took 0.012 ms]
[parsing function: Module._compile - took 0.084 ms]
[parsing function: exports.runInThisContext - took 0.031 ms]
[parsing script: /Users/mattzeunert/test.js - took 0.019 ms]
[parsing function: makeRequireFunction - took 0.037 ms]
[parsing function: sayHi - took 0.022 ms]
[parsing function: get - took 0.013 ms]
[parsing script: console.js - took 0.333 ms]
[parsing function: getStdout - took 0.108 ms]
[parsing function: createWritableStdioStream - took 0.098 ms]
[parsing script: tty.js - took 0.131 ms]
[parsing script: net.js - took 1.197 ms]
[parsing script: internal/net.js - took 0.032 ms]
[parsing function: protoGetter - took 0.021 ms]
[parsing function: WriteStream - took 0.040 ms]
[parsing function: Socket - took 0.083 ms]
[parsing function: Duplex - took 0.035 ms]
[parsing function: Readable - took 0.018 ms]
[parsing function: ReadableState - took 0.077 ms]
[parsing function: BufferList - took 0.010 ms]
[parsing function: Stream - took 0.013 ms]
[parsing function: EventHandlers - took 0.005 ms]
[parsing function: Writable - took 0.032 ms]
[parsing function: WritableState - took 0.066 ms]
[parsing function: CorkedRequest - took 0.029 ms]
[parsing function: once - took 0.018 ms]
[parsing function: _onceWrap - took 0.021 ms]
[parsing function: Readable.on - took 0.032 ms]
[parsing function: initSocketHandle - took 0.020 ms]
[parsing function: getStderr - took 0.031 ms]
[parsing function: $getMaxListeners - took 0.014 ms]
[parsing function: get - took 0.006 ms]
[parsing function: Console - took 0.052 ms]
[parsing function: Console.log - took 0.016 ms]
[parsing function: Console.warn - took 0.012 ms]
[parsing function: Console.dir - took 0.016 ms]
[parsing function: Console.time - took 0.009 ms]
[parsing function: Console.timeEnd - took 0.025 ms]
[parsing function: trace - took 0.018 ms]
[parsing function: Console.assert - took 0.014 ms]
[parsing function: installInspectorConsoleIfNeeded - took 0.036 ms]
[parsing function: exports.format - took 0.073 ms]
[parsing function: Socket.write - took 0.020 ms]
[parsing function: Writable.write - took 0.031 ms]
[parsing function: validChunk - took 0.029 ms]
[parsing function: writeOrBuffer - took 0.038 ms]
[parsing function: decodeChunk - took 0.015 ms]
[parsing function: doWrite - took 0.020 ms]
[parsing function: Socket._write - took 0.008 ms]
[parsing function: Socket._writeGeneric - took 0.063 ms]
[parsing function: _unrefTimer - took 0.012 ms]
[parsing function: exports._unrefActive - took 0.007 ms]
[parsing function: insert - took 0.028 ms]
[parsing function: createWriteReq - took 0.027 ms]
Hi Sparkle!
[parsing function: WritableState.onwrite - took 0.008 ms]
[parsing function: onwrite - took 0.026 ms]
[parsing function: onwriteStateUpdate - took 0.011 ms]
[parsing function: needFinish - took 0.010 ms]
[parsing function: nextTick - took 0.026 ms]
[parsing function: TickObject - took 0.011 ms]
[parsing function: _tickCallback - took 0.027 ms]
[parsing function: _combinedTickCallback - took 0.020 ms]
[parsing function: afterWrite - took 0.013 ms]
[parsing function: onwriteDrain - took 0.011 ms]
[parsing function: nop - took 0.003 ms]
[parsing function: finishMaybe - took 0.016 ms]
[parsing function: tickDone - took 0.017 ms]
[parsing function: emitPendingUnhandledRejections - took 0.034 ms]
$ d8 --trace-parse test.js 

test.js:3: ReferenceError: console is not defined
    console.log(message)
    ^
ReferenceError: console is not defined
    at sayHi (test.js:3:5)
    at test.js:6:1
function sayHi(name){
    var message = "Hi " + name + "!"
    console.log(message)
}

sayHi("Sparkle")
$ d8 --trace-parse test.js

[parsing script: native datetime-format-...js - took 3.453 ms]
[parsing function: ImportNow - took 0.025 ms]
[parsing function: InstallFunctions - took 0.035 ms]
[parsing function: SetFunctionName - took 0.020 ms]
[parsing script: native icu-case-mapping.js - took 0.029 ms]
[parsing function: OverrideFunction - took 0.311 ms]
[parsing function: PostExperimentals - took 0.024 ms]
[parsing script: test.js - took 1.248 ms]
[parsing function: sayHi - took 0.012 ms]
Hi Sparkle!
function sayHi(name){
    var message = "Hi " + name + "!"
    print(message)
}

sayHi("Sparkle")
$ d8 --trace-parse test.js

[parsing script: test.js - took 1.248 ms]
[parsing function: sayHi - took 0.012 ms]
Hi Sparkle!
function sayHi(name){
    var message = "Hi " + name + "!"
    print(message)
}

sayHi("Sparkle")
function sayHi(name){
    var message = "Hi " + name + "!"
    print(message)
}

function add(a, b){
    return a + b
}

sayHi("Sparkle")


=> Lazy Parsing

1
2

Lazy Parsing

$ d8 --trace-parse test.js

[parsing script: test.js - took 0.651 ms]
[parsing function: sayHi - took 0.009 ms]
Hi Sparkle!

1) pre-parse sayHi, add

2) full parse sayHi

function sayHi(name){
    var message = "Hi " + name + "!"
    print(message)
}

sayHi("Sparkle")
$ d8 --trace-parse test.js

[parsing script: test.js - took 1.248 ms]
[parsing function: sayHi - took 0.012 ms]
Hi Sparkle!
var constants = (function(){
    return {pi: 3.14}
})()
$ d8 --trace-parse test.js

[parsing script: test.js - took 0.024 ms]
var sayHi = (function sayHi(name){
    var message = "Hi " + name + "!"
    print(message)
})

sayHi("Sparkle")
$ d8 --trace-parse test.js

[parsing script: test.js - took 0.019 ms]
Hi Sparkle!

Create React App Bundle:
24ms => 18ms

press Shift 6 times to see all experiments

Execution

AST

Bytecode

Interpreter

Compiler

Ignition

Turbofan

$ d8 --trace_opt test.js

...
[marking 0x3830902c839 <JSFunction add (sfi = 0x3830902c489)>
    for optimized recompilation, reason: small function,
    ICs with typeinfo: 2/2 (100%), generic ICs: 0/2 (0%)]
[compiling method 0x3830902c839 <JSFunction add
    (sfi = 0x3830902c489)> using TurboFan]
...
[optimizing 0x3830902c839 <JSFunction add (sfi = 0x3830902c489)>
[completed optimizing 0x3830902c839 <JSFunction add ...>]
function add(a, b, c){
    return a + b + c
}

for (var i=0; i< 100000; i++){
    add(i, 1, 2)
}

Compiler Generates Optimized Machine Code Based On Type Data

$ d8 --trace_opt --trace_deopt test.js

...
[optimizing 0x25c3063ac879 <JSFunction add (sfi =
    0x25c3063ac489)> - took 1.146, 0.359, 0.014 ms]
[completed optimizing 0x25c3063ac879
     <JSFunction add (sfi = 0x25c3063ac489)>]
[deoptimizing (DEOPT eager): begin 0x25c3063ac879
      <JSFunction add (sfi = 0x25c3063ac489)> (opt #0) @3,
      FP to SP delta: 16, caller sp: 0x7fff559a1fc8]
function add(a, b, c){
    return a + b + c
}

for (var i=0; i< 100000; i++){
    add(i, 1, 2)
}

add(1, 2, "a")

Objects and Properties

person.age
age = person.age

age = [personAddress + offset]

age = [0x674836362 + 2]
Person
[0] firstName
[1] lastName
[2] age
[3] location

Hidden Classes

function Person(firstName, middleName, lastName) {

    this.firstName = firstName
    this.middleName = middleName
    this.lastName = lastName
}
var p1 = new Person("John", "William", "Green")
var p2 = new Person("Mary", "Lisa", "Smith")

Hidden Classes

var p1 = new Person("John", "William", "Green")
var p2 = new Person("Mary", "Lisa", "Smith")
p2.location = "London"
function getName(person){
    return person.firstName + person.lastName
}

var p1 = new Person("John", "William", "Green")
var p2 = new Person("Mary", "Lisa", "Smith")

for (var i=0;i<10000;i++){ getName(p1) }
for (var i=0;i<10000;i++){ getName(p2) }
var p1 = new Person("John", "William", "Green")
var p2 = new Person("Mary", "Lisa", "Smith")
p2.location = "London"

for (var i=0;i<10000;i++){ getName(p1) }
for (var i=0;i<10000;i++){ getName(p2) }
$ d8 --trace_deopt  test.js 

[deoptimizing (DEOPT eager): begin 0x52f9ab45 <JS Function getName (SharedFunctionInfo 0x52f9a829)> (opt #0) @2, FP to SP delta: 12, caller sp: 0xbff65004]
...
[deoptimizing (eager): end 0x52f9ab45 <JS Function getName (SharedFunctionInfo 0x52f9a829)> @2 => node=4, pc=0x3ad0756f, caller sp=0xbff65004, state=NO_REGISTERS, took 0.104 ms]
[removing optimized code for: getName]
var p1 = new Person("John", "William", "Green")
p1.location = null
var p2 = new Person("Mary", "Lisa", "Smith")
p2.location = "London"

for (var i=0;i<10000;i++){ getName(p1) }
for (var i=0;i<10000;i++){ getName(p2) }
$ d8 --trace_deopt  test.js 

No output :)

Objects of the same type should have the same properties

Memory

var p1 = new Person("John", "William", "Green")
var p2 = new Person("Mary", "Lisa", "Smith")

1

2

3

4

5

5 * (64 / 8) = 40

var p = [];
for (var i=0; i<100000;i++){
    p.push(new Person("a", "b", "c"))
}
p1.location = "London"
p1.otherProp = "sth"

Extra Properties

p1[0] = "zero"
p1[1] = "one"

Numbered Properties

Map
extraProperties
elements
firstName
middleName
lastName
location
otherProp

Person Object

0
1

1

2

3

4

5

6

 6 * (64/8) = 48

delete p2.middleName

Arrays


var arr = []
for (var i=0; i< 1000000;i++){
   arr.push(Math.random())
}

9.7MB ≈ 8MB 

0.2 1.8 1.4
0x8789 0x37b2 0xae72

"abc"

(reference)

0xc79d

(reference) (reference) (reference)
0x127b 0x7a55 0x98ee

"abc"

(reference)

0xc79d

{ num: 0.2 }

{ num: 1.8 }

{ num: 1.4 }

arr.push("abc")
arr.push("abc")

25.7MB ≈ 24MB 

V8 Wiki - Design Elements

V8 Behind the Scenes and a Tale of TurboFan

JavaScript Start-up Performance

 

How V8 runs JavaScript - FullStack JS

By Matt Zeunert

How V8 runs JavaScript - FullStack JS

  • 1,276