01 Jun 2017
In a previous post, Notes on how browsers work, we studied the high-level architecture of a browser, specifically the Rendering Engine. We used the following diagram,
A Job is an abstract operation that initiates an ECMAScript computation when no other ECMAScript computation is currently in progress. A Job abstract operation may be defined to accept an arbitrary set of job parameters. Execution of a Job can be initiated only when there is no running execution context and the execution context stack is empty. A PendingJob is a request for the future execution of a Job. A PendingJob is an internal Record whose fields are specified in Table 25. Once execution of a Job is initiated, the Job always executes to completion. No other Job may be initiated until the currently running Job completes. However, the currently running Job or external events may cause the enqueuing of additional PendingJobs that may be initiated sometime after completion of the currently running Job. MDN’s Concurrency model and Event Loop  describes this spec in a more friendly way. As in other programming environments such as C and Java, we have two types of memory available: the heap and the stack. The heap is the general purpose memory and the stack is where we keep track of scopes for example when doing function calls.
The execution model is also called the event loop because of the high-level working of this model:
When the stack is empty, the runtime processes the first message from the queue. While executing the corresponding function, it adds an initial scope to the stack. The function is guaranteed to execute “atomically”, that is, until it returns. The execution might cause new messages to be enqueued, for example if we call
The callback passed as the first argument to
setTimeout() will be enqueued, not stacked. On the other hand, if we have a recursive function, such as a factorial:
The recursive calls should all go to the same stack, so they will be executed to completion, that is until the stack is empty. The calls provided to
setTimeout() will get enqueued and be executed only after the value of the factorial gets printed.
In fact, using
setTimeout() is a common trick to control the execution order of functions. For example, say that function
B calls another function
C normally, then
C will have to finish before
B returns, as the sample code below:
In case we want to finish executing
A first, then
B can call
setTimeout(C, 0), because then it will be enqueued, and then both
B will finish until a new message is processed from the queue, as the code below:
We discussed Web Workers in a previous post, in which we mentioned that it’s a separate thread that shares no memory with the main thread. In fact, according to , it has its own stack, heap and queue. It doesn’t violate the execution model of the main thread because communications must be done via a publisher/subscriber API, which means communicating between two threads is subject to the queueing.
Fast Property Access
Object can be mutated (added and removed properties) in runtime. Implementing them as hashtables can lead to performance problems when accessing properties of these structures. Compare that to compiled languages like Java in which instances of a class can be allocated with all its members in a single chunk of memory and accessing properties of objects consists in adding an offset to the object’s pointer.
V8 optimizes the
Object allocation by creating hidden classes. It makes use of the fact that properties are mutated in the same pattern. For example in
In this case, we always insert the property
x and then
y. V8 starts with an empty class
C0 when the object is first created. When x is assigned, it creates another class
C1 with property
x and that points to
y is assigned, it creates yet another class
C2 with property
y that point to
Point constructor is finished, it will be an instance of
C2. It has to instantiate 3 objects one from
y is an adding an offset operation, while accessing property
x is two offset operations, but still fast than a table lookup. Other instances of
Point will share the same class
C2. If for some reason we have a point with only
x set, then it will be an instance of class
C1. This sharing of structures resembles the persistent data structures that we studied previously.
Another interesting property of this method is that it doesn’t need to know in advance whether the code has indeed a pattern in mutating objects. It’s sort of a JIT compilation.
Dynamic Machine Code Generation
According to :
Efficient Garbage Collection
We wrote about V8’s memory management in a previous post.