V8
V8 is Google's JavaScript engine, written in C++. It compiles and executes JavaScript code inside of a VM (Virtual Machine). When a webpage loaded into Google Chrome demonstrates some sort of dynamic effect, like automatically updating a list or news feed, you are seeing JavaScript, compiled by V8, at work.
While Node itself will efficiently manage I/O operations, its process
object refers to the V8 runtime. As such, it is important to understand how to configure the V8 environment, especially as your application grows in size.
By typing node -h
into a console, something like the following will be displayed:
We can see how a list of V8 options is accessible via the –-v8-options
flag.
The list of configuration options for V8 is a long one, so we're not going to cover each option here. As we progress through the book, relevant options will be discussed with more depth. It is nevertheless useful to summarize some of the options provided for managing system limits and memory, as well as those used to configure JavaScript's command set, introducing some of the new features in ES6 (EcmaScript6), often referred to as Harmony.
The version of V8 used by your Node installation can be viewed by typing:
node –e "console.log(process.versions.v8)"
Memory and other limits
One very powerful V8 configuration option is important enough to make it into Node's own collection: --max-stack-size
. Let's look into some of the new powers a Node developer has been given in being able to configure a specific JavaScript runtime.
Trying to break a system is an excellent way to discover its limits and shape. Let's write a program that will crash V8:
var count = 0; (function curse() { console.log(++count); curse(); })()
This self-contained, self-executing function will recursively call itself forever, or until it is forced to stop. Each iteration of curse
adds another frame to the call stack. This uncontrolled growth will eventually cause the JavaScript runtime to collapse, citing a RangeError: Maximum call stack size exceeded
.
The purpose of --max-stack-size
should now be clear. The direct V8 option equivalent is –-stack_size
, which is passed a value, in KB (Kilobytes), to raise this limit. Experiment with the above program, noting the number of iterations possible at different settings.
While it is likely that hitting this limit represents an incorrectly designed algorithm, being able to expand the width of the operating space available to a Node process adds to the collection of solutions available to developers.
On 32 bit and 64 bit machines V8's memory allocation defaults are, respectively, 700 MB and 1400 MB. In newer versions of V8, memory limits on 64 bit systems are no longer set by V8, theoretically indicating no limit. However, the OS (Operating System) on which Node is running can always limit the amount of memory V8 can take, so the true limit of any given process cannot be generally stated.
V8 makes available the --max_old_space_size
option, which allows control over the amount of memory available to a process, accepting a value in MB. Should you need to increase memory allocation, simply pass this option the desired value when spawning a Node process.
It is often an excellent strategy to reduce the available memory allocation for a given Node instance, especially when running many instances. As with stack limits, consider whether massive memory needs are better delegated to a dedicated storage layer, such as an in-memory database or similar.
Note
An informative discussion with the V8 team regarding their views on how memory should be allocated can be found here:
One of the key advantages of modern high-level languages such as JavaScript is the automatic management of memory through GC (Garbage Collection). GC strategies are many and complex, yet all follow a simple core idea: every so often, free allocated memory that is no longer being used.
The drawback of automatic GC is that it puts a slight brake on process speed. While the clear advantages of automatic GC outweigh its drawbacks in the majority of cases, there remains for the Node developer an opportunity to control some of its behavior. This is primarily done via the flags –-nouse_idle_notification
and –-expose_gc
.
Passing the –-nouse_idle_notification
flag will tell V8 to ignore idle notification calls from Node, which are requests to V8 asking it to run GC immediately, as the Node process is currently idle. Because Node is aggressive with these calls (efficiency breeds clean slates), an excess of GC may slow down your application. Note that using this flag does not disable GC; GC simply runs less often. In the right circumstances this technique can increase performance.
--expose_gc
introduces a new global method to the Node process, gc()
, which allows JavaScript code to manually start the GC process. In conjunction with –-nouse_idle_notification
the developer can now control to some degree how often GC runs. At any point in my JavaScript code I can simply call gc()
and start the collector.
Being able to adjust memory usage and GC is certainly useful. Remember that application volume can rapidly rise in unpredictable ways. If the memory footprint of your application holds steady near the very limits of V8's allocation, you should begin to think about scaling horizontally. Use memory wisely, and split off new Node instances where appropriate.
Harmony
JavaScript has never stopped evolving and it is now experiencing something of a renaissance, helped in no small part by the popularity of Node. The language's next version, named Harmony, introduces some significant new features and concepts.
Note
More information on ES6 Harmony can be found at: http://wiki.ecmascript.org/doku.php?id=harmony:harmony.
The available Harmony options are:
Flag |
Description |
---|---|
|
Enable semantics for typeof |
|
Enable block scoping |
|
Enable modules (implies block scoping) |
|
Enable proxies |
|
Enable collections (sets, maps, and weak maps) |
|
Enable all features (except typeof) |
It is beyond the scope of this book to discuss these new features in any depth. Nonetheless, it should be stated that this ability to use the very latest JavaScript features, now, through Node, offers the developer a great advantage—there is no browser war on a server to hold back innovation.
For example, ES6's weakMap
allows the use of non-strings as keys in a HashMap:
"use strict" let owners = new WeakMap(); let task = { title : "Big Project" }; owners.set(task, 'John'); function owner(task) { if(owners.has(task)) { return console.log(owners.get(task)); } console.log("No owner for this task."); } owner(task); // "John" owner({}); // "No owner for this task"
As an exercise, the reader might map (fixed) input streams to (variable) output streams in a similar manner.