Coding With Fun
Home Docker Django Node.js Articles FAQ

Learn more about JavaScript V8


May 31, 2021 Article blog


Table of contents


This article comes from the public number: CoyPan, co-author of "CoyPan" in line with expectations

Original title: A Deep Dive Into V8

Original link: blog.appsignal.com/2020/07/01/a-deep-dive-into-v8.html?utm_source=javascript-weekly-sponsored&utm_medium=email&utm_campaign=deep-dive-v8&utm_content=sponsored-link

The body begins

Most front-end developers come across a buzzword: V8 It's popular in large part because it takes JavaScript's performance to the next level.

Yes, V8 is fast. B ut how does it play its magic? Why is it responding so quickly?

"The V8 is Google's open source high-performance JavaScript and WebAssembly engine, written in C++ the official documents state. It's mainly used in Chrome and Node.js and so on.

In other words, V8 is software developed by C++ that compiles JavaScript into executable code, the machine code.

Now, let's start to see more clearly that Chrome and Node.js just a bridge to deliver JS code to its final destination: machine code running on a particular machine.

Another important role in V8 performance is its generational and ultra-accurate garbage collector. It is optimized to collect objects that JavaScript no longer needs with low memory.

In addition, V8 relies on a set of other tools and features to improve some of JS's inherent functionality. These features tend to slow down JS (such as the dynamic nature of JS).

In this article, we'll explore these tools (Ignition and TurboFan) and features in more detail. In addition, we'll cover the basics of V8 internal capabilities, compilation and garbage collection processes, and single-threaded features.

Start with the basics

How does the machine code work? Simply put, machine code is a very low-level set of instructions that are executed in a specific part of machine memory.

The process of C++ a machine code, for example, is like this:

 Learn more about JavaScript V81

Before going any further, it is important to note that this is a compilation process that differs from the JavaScript interpretation process. In fact, the compiler generates a complete program at the end of the process, and the interpreter works as a program itself, completing tasks by reading instructions (usually scripts, such as JavaScript scripts) and converting them into executable commands.

The interpretation process can be dynamic (the interpreter parses and only runs the current command) or fully parsed (that is, the interpreter translates the script completely before proceeding with the appropriate machine instructions).

Back in the diagram, the compilation process usually starts with the source code. Y ou implement the code, save it, and run it. T he running process starts with the compiler in turn. T he compiler is a program that, like other programs, runs on your machine. I t then traverses all the code and generates the object file. T hose files are machine codes. They are optimization code that runs on a specific machine, which is why you must use a specific compiler when you move from one operating system to another.

But you can't execute individual object files, you need to combine them into a single file, the well-known .exe file (executable). This is Linker job.

Finally, Loader is the agent responsible for transferring code from the exe file to the virtual memory of the operating system. I t is basically a means of transport. Here, your program finally starts running.

Sounds like a long process, doesn't it?

Most of the time (unless you're a developer using assembly on a bank mainframe), you'll spend time programming in high-level languages: Java, C#, Ruby, JavaScript, and so on.

The higher the language, the slower it is. That's why C and C++ faster because they're very close to the machine code language: assembly language.

In addition to performance, one of the main advantages of V8 is the possibility of exceeding ECMAScript standard and understanding C++

 Learn more about JavaScript V82

JavaScript is limited to ECMAScript The V8 engine, in order to exist, must be compatible, but not limited to JavaScript.

It's great to have the ability to integrate the features of C++ into V8 Because C++ special nature of file processing and memory/threading that has evolved to very good OS operations, it is useful to have all of these capabilities in JavaScript.

If you think about it, Node.js itself was born in a similar way. It follows a path similar to V8 plus server and network functionality.

Single thread

If you are a Node developer, you should be familiar with the single-threaded nature of V8 A JS execution context is proportional to the number of threads.

Of course, V8 manages the operating system threading mechanism in the background. It can work with multiple threads because it is a complex software that can perform many tasks at the same time.

However, V8 creates only a single-threaded environment for each JavaScript execution context. The rest is under V8 control.

Imagine the stack of function calls that JavaScript code should make. ript works by stacking one function on top of another, following the order in which each function is inserted/called. W e can't know if it calls other functions until we reach the contents of each function. If this happens, the called function is placed behind the caller in the stack.

For example, when callbacks are involved, they are placed at the end of the stack.

Managing the memory required for this stack organization and process is one of V8 main tasks.

Ignition and TurboFan

Since release 5.9 in May 2017, V8 has come with a new JavaScript execution pipeline built on top of V8 interpreter Ignition It also includes an update and a better optimization compiler -TurboFan

These changes are entirely focused on overall performance and the difficulties Google developers face in adapting their engines to all the rapid and significant changes in the JavaScript space.

From the beginning of the project, V8 maintainers have been worried about finding a good way to improve V8 performance while JavaScript continues to evolve.

Now that we can see the Benchmarks test Benchmarks for the new engine, there's been a huge improvement:

 Learn more about JavaScript V83

Hidden Classes (Hidden Class)

This is another magic trick for V8. ript is a dynamic language. T his means that new properties can be added, replaced, and deleted during execution. For example, in a language like Java, this is not possible, Java everything (classes, methods, objects, and variables) must be defined before the program executes and cannot be changed dynamically after the application starts.

Because of its special nature, JavaScript interpreters typically perform dictionary lookups based on hash functions (hash algorithms) to know exactly where this variable or object is allocated in memory.

This is expensive for the last process. I n other languages, when objects are created, they receive an address (pointer) as one of their implicit properties. This allows us to know exactly where they are in memory and how much space to allocate.

For JavaScript, this is not possible because we cannot map content that does not exist. That's where Hidden Classes comes into play.

Hidden classes are almost identical to classes in Java static and fixed classes have unique addresses to locate them. However, V8 is not executed before the program executes, but each time the object structure changes "dynamically" during the run.

Let's look at an example to illustrate the problem. Consider the following snippets of code:

function User(name, fone, address) {
   this.name = name
   this.phone = phone
   this.address = address
}

In the prototype-based feature of JavaScript, each time a new user object is instantiated, assume:

var user = new User("John May", "+1 (555) 555-1234", "123 3rd Ave")

V8 then creates a new hidden class. We call it _User0

 Learn more about JavaScript V84

Each object has a reference in memory to its class representation. I t is a class pointer. A t this point, because we've just instantiated a new object, we've created only one hidden class in memory. It's empty now.

When you execute the first line of code in this function, a new hidden class is created on top of the previous one, this time _User1

 Learn more about JavaScript V85

It is basically the memory address of User with the name property. In our example, we didn't use user name only as a property, but each time we do this, this is the hidden class that V8 will load as a reference.

name property is added to the offset of the memory buffer by 0, which means that this is considered the first property in the final order.

V8 also adds a conversion value to _User0 hidden class. This helps the interpreter understand that each time you add a name property to a User object, you must handle the conversion from _User0 to _User1

When the second line in the function is called, the same procedure occurs again and a new hidden class is created:

 Learn more about JavaScript V86

You can see the hidden class trace stack. In a chain maintained by a converted value, one hidden class leads to the other.

The order in which properties are added determines how many hidden classes V8 will create. I f you change the order of the lines in the snippets we create, different hidden classes will also be created. That's why some developers try to keep the order in which hidden classes are reused, reducing overhead.

Inline Caching (Inline Cache)

This is a very common term in the JUST-in-Time compiler. It is directly related to the concept of hidden classes.

For example, whenever you call a function and pass an object as an argument, V8 sees the action and thinks, "Well, this object was successfully passed two or more times as an argument... W hy not store it in my cache for future calls instead of performing the entire time-consuming hidden class validation process again?

Let's review the previous example:

function User(name, fone, address) { // Hidden class _User0
   this.name = name // Hidden class _User1
   this.phone = phone // Hidden class _User2
   this.address = address // Hidden class _User3
}

When we pass an instance of the User object to the function twice as an argument, V8 jumps to the hidden class lookup and goes directly to the property of the offset. This is much faster.

However, keep in mind that changing the order in which any property assignments in a function results in different hidden classes, so V8 will not be able to use inline caching.

This is a good example of how developers should not avoid gaining a deeper understanding of the engine. Instead, having this knowledge will help your code execute better.

Garbage Collecting (Garbage Collection)

Do you remember when we mentioned that V8 collects memory garbage in another thread? This is helpful because our program execution is not affected.

V8 uses a well-known "tag and scan" policy to collect old objects in memory. In this strategy, the stage at which GC scans memory objects to "tag" them for collection is a bit slow because it requires pausing code execution.

However, V8 is incremental, which means that V8 tries to mark as many objects as possible for each GC pause. I t makes everything faster because you don't need to stop the entire execution until the collection is finished. In large applications, performance improvements vary greatly.

That's what W3Cschool编程狮 has learned about JavaScript V8, and I hope it will help you.