Tìm hiểu về V8 Engine trong Chrome và Node.js
Trong bài viết này, chúng ta sẽ cùng khám phá V8 Engine, một JavaScript Engine rất phổ biến được sử dụng trong trình duyệt Chrome và môi trường Node.js. Các bạn có thể tham khảo bài viết trước đó để hiểu rõ hơn về kiến trúc trình duyệt web và vai trò của JavaScript Engine trong Runtime Environment.

Mở Đầu
Như đã đề cập, JavaScript Engine có nhiệm vụ phân tích, biên dịch và thực thi mã JavaScript. V8 không chỉ biên dịch mã Javascript mà còn tối ưu hóa hiệu suất để các ứng dụng hoạt động nhanh hơn.
Lưu ý: Chrome và Node.js đều được gọi là JavaScript Runtime Environment.
V8 Engine: Các Thành Phần Chính và Chức Năng
V8 Engine có các thành phần cơ bản gồm:
- Parser (Bộ phân tích cú pháp)
- Ignition Interpreter (Trình thông dịch)
- TurboFan (JIT Compiler)
- Garbage Collector (Bộ thu gom rác)
- Call Stack
- Heap Memory
1. Parser (Bộ phân tích cú pháp)
Bộ phân tích cú pháp chuyển đổi mã JavaScript từ dạng chuỗi văn bản thành cấu trúc dữ liệu mà V8 Engine có thể hiểu. Quá trình này chia thành hai bước:
- Lexical Analysis (Tokenizer/Lexer): Tách mã nguồn thành các token, như từ khóa, toán tử và biến.
- Syntax Analysis (Parser): Tạo ra AST (Abstract Syntax Tree - Cây cú pháp trừu tượng) từ các token đã phân tích.
Ví dụ:
let x = 4 + 5;
AST từ mã nguồn trên sẽ như sau:
VariableDeclaration
├── Identifier (x)
├── Assignment (=)
├── BinaryExpression (+)
├── Number (4)
├── Number (5)
2. Ignition Interpreter (Trình thông dịch)
Ignition Interpreter
sẽ đọc AST và chuyển đổi thành Bytecode (mã trung gian tối ưu) để thực thi. Bytecode là định dạng trung gian, tối ưu hơn so với mã nguồn ban đầu.
3. TurboFan (JIT Compiler)
Khi một đoạn mã được thực thi nhiều lần, V8 sẽ nhận diện đoạn mã đó là quan trọng và cần tối ưu hóa. TurboFan sẽ biên dịch các đoạn mã Bytecode này thành mã máy tối ưu hóa, giúp tăng tốc độ thực thi.
3.1. Nhúng trực tiếp các hàm nhỏ (Inlining)
Khi một hàm được gọi nhiều lần, TurboFan có thể nhúng mã hàm vào nơi gọi, tránh việc gọi hàm gây tốn tài nguyên.
Ví dụ:
function add(a, b) {
return a + b;
}
function calc(x, y) {
return add(x, y) * 2;
}
Trước tối ưu hóa: calc(x, y)
cần gọi add(x, y)
, gây tốn thời gian.
Sau tối ưu hóa: add(x, y)
có thể được thay bằng x + y
, tránh việc gọi hàm.
3.2. Loại bỏ mã không cần thiết (Dead Code Elimination)
TurboFan
sẽ loại bỏ các biến hoặc đoạn mã không ảnh hưởng đến kết quả.
Ví dụ:
function test() {
let x = 10;
let y = x + 5;
return x; // y không được sử dụng => loại bỏ y khi biên dịch
}
3.3. Loop Unrolling (Tối ưu vòng lặp)
Khi số lần lặp được xác định và nhỏ, TurboFan có thể chuyển đổi vòng lặp thành các lệnh tuần tự, tránh việc kiểm tra điều kiện lặp nhiều lần.
Ví dụ:
for (let i = 0; i < 3; i++) {
console.log(i);
}
Sau tối ưu hóa:
console.log(0);
console.log(1);
console.log(2);
4. Garbage Collector (Bộ thu gom rác)
Garbage Collector dọn dẹp bộ nhớ bằng cách xóa các đối tượng không còn được sử dụng nữa. V8 sử dụng thuật toán Mark-and-Sweep:
- Mark (Đánh dấu): Đánh dấu các đối tượng nào vẫn đang được tham chiếu.
- Sweep (Dọn dẹp): Giải phóng bộ nhớ của các đối tượng không còn tham chiếu.
Ví dụ:
function generate() {
let obj = { name: "Viblo" };
}
generate(); // obj bị xóa khỏi bộ nhớ sau khi hàm generate() kết thúc
5. Call Stack
Call Stack là cấu trúc dữ liệu dùng để quản lý lời gọi hàm trong quá trình thực thi mã JavaScript. Hoạt động theo nguyên tắc LIFO (Last In, First Out): hàm nào vào sau thì sẽ được thực thi trước.
Ví dụ:
function first() {
console.log("First function");
}
function second() {
first();
console.log("Second function");
}
function third() {
second();
console.log("Third function");
}
third();
| Cách Call Stack hoạt động | |---------------------------| | 1. third()
được đưa vào Call Stack. | | 2. second()
được gọi trong third()
, do đó cũng được đẩy vào Stack. | | 3. first()
được gọi trong second()
, và cũng được đẩy vào Stack. | | 4. console.log("First function")
thực thi → "First function" in ra màn hình. | | 5. first()
hoàn thành, bị loại khỏi Call Stack. | | 6. console.log("Second function")
thực thi → "Second function" in ra. | | 7. second()
hoàn thành, bị loại khỏi Call Stack. | | 8. console.log("Third function")
thực thi → "Third function" in ra. | | 9. third()
hoàn thành, bị loại khỏi Call Stack. |
6. Heap Memory
Heap Memory là vùng nhớ dùng để lưu trữ dữ liệu động như: Object, Array, Function. Vùng nhớ này giúp lưu trữ dữ liệu lâu hơn so với Stack.
Ví dụ:
let x = 1; // Lưu trong Stack (giá trị nguyên thủy)
let obj = { a: 1 }; // Lưu trong Heap (đối tượng)

Kết Luận
Chúng ta đã tìm hiểu về V8, một JavaScript Engine quan trọng, cũng như các thành phần và chức năng của nó. Bạn sẽ tự tin hơn khi viết mã JavaScript và có cái nhìn rõ ràng hơn về cách ngôn ngữ này được xử lý và thực thi. Hy vọng bài viết sẽ giúp ích cho bạn trong quá trình học tập và phát triển! 🥰
Comments ()