V8#

原始碼: lib/v8.js

node:v8 模組公開特定於內建於 Node.js 二進位檔的 V8 版本的 API。可以使用下列方式存取

const v8 = require('node:v8'); 

v8.cachedDataVersionTag()#

傳回一個整數,代表從 V8 版本、命令列旗標和偵測到的 CPU 功能衍生的版本標記。這對於判斷 vm.Script cachedData 緩衝區是否與這個 V8 執行個體相容很有用。

console.log(v8.cachedDataVersionTag()); // 3947234607
// The value returned by v8.cachedDataVersionTag() is derived from the V8
// version, command-line flags, and detected CPU features. Test that the value
// does indeed update when flags are toggled.
v8.setFlagsFromString('--allow_natives_syntax');
console.log(v8.cachedDataVersionTag()); // 183726201 

v8.getHeapCodeStatistics()#

取得堆積中程式碼及其元資料的統計資料,請參閱 V8 GetHeapCodeAndMetadataStatistics API。傳回一個具有下列屬性的物件

{
  code_and_metadata_size: 212208,
  bytecode_and_metadata_size: 161368,
  external_script_source_size: 1410794,
  cpu_profiler_metadata_size: 0,
} 

v8.getHeapSnapshot([options])#

  • options <Object>

    • exposeInternals <boolean> 如果為 true,會在堆快照中公開內部結構。預設值:false
    • exposeNumericValues <boolean> 如果為 true,會在人工欄位中公開數值。預設值:false
  • 傳回值:<stream.Readable> 包含 V8 堆快照的 Readable。

產生目前 V8 堆的快照,並傳回一個 Readable Stream,可透過它來讀取 JSON 序列化表示。此 JSON 串流格式旨在與 Chrome DevTools 等工具搭配使用。JSON 架構未記錄且特定於 V8 引擎。因此,架構可能會隨著 V8 版本而有所不同。

建立堆快照需要約為堆大小兩倍的記憶體,在建立快照時。這會導致 OOM 殺手終止程序的風險。

產生快照是同步作業,會根據堆大小封鎖事件迴圈一段時間。

// Print heap snapshot to the console
const v8 = require('node:v8');
const stream = v8.getHeapSnapshot();
stream.pipe(process.stdout); 

v8.getHeapSpaceStatistics()#

傳回 V8 堆空間的統計資料,也就是組成 V8 堆的區段。堆空間的排序和堆空間的可用性都無法保證,因為統計資料是透過 V8 GetHeapSpaceStatistics 函數提供的,且可能會隨著 V8 版本而有所不同。

傳回的值是一個包含以下屬性的物件陣列

[
  {
    "space_name": "new_space",
    "space_size": 2063872,
    "space_used_size": 951112,
    "space_available_size": 80824,
    "physical_space_size": 2063872
  },
  {
    "space_name": "old_space",
    "space_size": 3090560,
    "space_used_size": 2493792,
    "space_available_size": 0,
    "physical_space_size": 3090560
  },
  {
    "space_name": "code_space",
    "space_size": 1260160,
    "space_used_size": 644256,
    "space_available_size": 960,
    "physical_space_size": 1260160
  },
  {
    "space_name": "map_space",
    "space_size": 1094160,
    "space_used_size": 201608,
    "space_available_size": 0,
    "physical_space_size": 1094160
  },
  {
    "space_name": "large_object_space",
    "space_size": 0,
    "space_used_size": 0,
    "space_available_size": 1490980608,
    "physical_space_size": 0
  }
] 

v8.getHeapStatistics()#

傳回一個包含以下屬性的物件

does_zap_garbage 是 0/1 布林值,表示是否啟用 --zap_code_space 選項。這會讓 V8 以位元模式覆寫堆疊垃圾。RSS 佔用空間(常駐集大小)會變大,因為它會持續觸及所有堆疊頁面,這會讓作業系統不太可能將它們換出。

number_of_native_contexts native_context 的值是目前活動中的頂層內容數。此數字隨著時間增加表示記憶體外洩。

number_of_detached_contexts detached_context 的值是已分離且尚未垃圾回收的內容數。此數字非零表示潛在的記憶體外洩。

total_global_handles_size total_global_handles_size 的值是 V8 全域控制代碼的總記憶體大小。

used_global_handles_size used_global_handles_size 的值是 V8 全域控制代碼已使用的記憶體大小。

external_memory external_memory 的值是陣列緩衝區和外部字串的記憶體大小。

{
  total_heap_size: 7326976,
  total_heap_size_executable: 4194304,
  total_physical_size: 7326976,
  total_available_size: 1152656,
  used_heap_size: 3476208,
  heap_size_limit: 1535115264,
  malloced_memory: 16384,
  peak_malloced_memory: 1127496,
  does_zap_garbage: 0,
  number_of_native_contexts: 1,
  number_of_detached_contexts: 0,
  total_global_handles_size: 8192,
  used_global_handles_size: 3296,
  external_memory: 318824
} 

v8.setFlagsFromString(flags)#

v8.setFlagsFromString() 方法可用於以程式化方式設定 V8 命令列旗標。此方法應謹慎使用。在 VM 啟動後變更設定可能會導致無法預測的行為,包括當機和資料遺失;或可能什麼都不做。

可透過執行 node --v8-options 來確定 Node.js 版本可用的 V8 選項。

用法

// Print GC events to stdout for one minute.
const v8 = require('node:v8');
v8.setFlagsFromString('--trace_gc');
setTimeout(() => { v8.setFlagsFromString('--notrace_gc'); }, 60e3); 

v8.stopCoverage()#

v8.stopCoverage() 方法允許使用者停止由 NODE_V8_COVERAGE 開始的涵蓋範圍收集,以便 V8 可以釋放執行計數記錄並最佳化程式碼。如果使用者想要依需求收集涵蓋範圍,則這可以與 v8.takeCoverage() 搭配使用。

v8.takeCoverage()#

v8.takeCoverage() 方法允許使用者依需求將由 NODE_V8_COVERAGE 開始的涵蓋範圍寫入磁碟。此方法可以在處理程序的生存期中多次呼叫。每次執行計數器都會重設,並且新的涵蓋範圍報告將寫入由 NODE_V8_COVERAGE 指定的目錄。

當處理程序即將結束時,除非在處理程序結束前呼叫 v8.stopCoverage(),否則最後一個涵蓋範圍仍會寫入磁碟。

v8.writeHeapSnapshot([filename[,options]])#

  • filename <string> 要儲存 V8 堆疊快照的檔案路徑。如果未指定,將產生具有模式 'Heap-${yyyymmdd}-${hhmmss}-${pid}-${thread_id}.heapsnapshot' 的檔案名稱,其中 {pid} 將是 Node.js 處理程序的 PID,{thread_id} 將是 0(當從 Node.js 主執行緒呼叫 writeHeapSnapshot() 時)或工作執行緒的 ID。
  • options <Object>
    • exposeInternals <boolean> 如果為 true,會在堆快照中公開內部結構。預設值:false
    • exposeNumericValues <boolean> 如果為 true,會在人工欄位中公開數值。預設值:false
  • 傳回:<string> 儲存快照的檔案名稱。

產生目前 V8 堆疊的快照,並將其寫入 JSON 檔案。此檔案旨在與 Chrome DevTools 等工具搭配使用。JSON 架構未記載,且特定於 V8 引擎,且可能會隨著 V8 版本的不同而有所變更。

堆疊快照特定於單一 V8 隔離。使用 工作執行緒 時,從主執行緒產生的堆疊快照將不包含任何關於工作執行緒的資訊,反之亦然。

建立堆快照需要約為堆大小兩倍的記憶體,在建立快照時。這會導致 OOM 殺手終止程序的風險。

產生快照是同步作業,會根據堆大小封鎖事件迴圈一段時間。

const { writeHeapSnapshot } = require('node:v8');
const {
  Worker,
  isMainThread,
  parentPort,
} = require('node:worker_threads');

if (isMainThread) {
  const worker = new Worker(__filename);

  worker.once('message', (filename) => {
    console.log(`worker heapdump: ${filename}`);
    // Now get a heapdump for the main thread.
    console.log(`main thread heapdump: ${writeHeapSnapshot()}`);
  });

  // Tell the worker to create a heapdump.
  worker.postMessage('heapdump');
} else {
  parentPort.once('message', (message) => {
    if (message === 'heapdump') {
      // Generate a heapdump for the worker
      // and return the filename to the parent.
      parentPort.postMessage(writeHeapSnapshot());
    }
  });
} 

v8.setHeapSnapshotNearHeapLimit(limit)#

穩定性:1 - 實驗性

如果已從命令列設定 --heapsnapshot-near-heap-limit 或 API 已呼叫多次,則 API 為空操作。limit 必須為正整數。請參閱 --heapsnapshot-near-heap-limit 以取得更多資訊。

序列化 API#

序列化 API 提供序列化 JavaScript 值的方法,這種方法與 HTML 結構化複製演算法 相容。

此格式向下相容(亦即可以安全儲存到磁碟)。相等的 JavaScript 值可能會產生不同的序列化輸出。

v8.serialize(value)#

使用 DefaultSerializervalue 序列化成一個緩衝區。

當嘗試序列化一個需要比 buffer.constants.MAX_LENGTH 還要大的緩衝區的巨大物件時,將會擲回 ERR_BUFFER_TOO_LARGE

v8.deserialize(buffer)#

使用具有預設選項的 DefaultDeserializer 從緩衝區讀取 JS 值。

類別:v8.Serializer#

new Serializer()#

建立新的 Serializer 物件。

serializer.writeHeader()#

寫出標頭,其中包含序列化格式版本。

serializer.writeValue(value)#

序列化 JavaScript 值,並將序列化的表示形式新增至內部緩衝區。

如果無法序列化 value,則會擲回錯誤。

serializer.releaseBuffer()#

傳回儲存的內部緩衝區。一旦緩衝區被釋放,就不應再使用此序列化器。如果先前的寫入失敗,呼叫此方法會導致未定義的行為。

serializer.transferArrayBuffer(id, arrayBuffer)#

標記 ArrayBuffer 的內容已傳輸出頻帶外。將對應的 ArrayBuffer 傳遞給反序列化內容中的 deserializer.transferArrayBuffer()

serializer.writeUint32(value)#

寫入原始 32 位元無符號整數。用於自訂 serializer._writeHostObject() 內部。

serializer.writeUint64(hi, lo)#

寫入原始 64 位元無符號整數,拆分為高低 32 位元部分。用於自訂 serializer._writeHostObject() 內部。

serializer.writeDouble(value)#

寫入 JS 數字 值。用於自訂 serializer._writeHostObject() 內部。

serializer.writeRawBytes(buffer)#

將原始位元組寫入序列化器的內部緩衝區。反序列化器需要一種方式來計算緩衝區的長度。用於自訂 serializer._writeHostObject() 內部。

serializer._writeHostObject(object)#

呼叫此方法以寫入某種主機物件,即由原生 C++ 繫結建立的物件。如果無法序列化 object,應擲出適當的例外狀況。

此方法本身不存在於 Serializer 類別中,但子類別可以提供。

serializer._getDataCloneError(message)#

呼叫此方法以產生當無法複製物件時將擲出的錯誤物件。

此方法預設為 Error 建構函式,且可以在子類別中覆寫。

serializer._getSharedArrayBufferId(sharedArrayBuffer)#

當序列化器準備序列化一個 SharedArrayBuffer 物件時,會呼叫此方法。它必須傳回物件的無符號 32 位元整數 ID,如果此 SharedArrayBuffer 已序列化,則使用相同的 ID。在反序列化時,此 ID 會傳遞給 deserializer.transferArrayBuffer()

如果無法序列化物件,則應擲回例外。

此方法本身不存在於 Serializer 類別中,但子類別可以提供。

serializer._setTreatArrayBufferViewsAsHostObjects(flag)#

指出是否將 TypedArrayDataView 物件視為主機物件,亦即將它們傳遞給 serializer._writeHostObject()

類別:v8.Deserializer#

new Deserializer(buffer)#

建立新的 Deserializer 物件。

deserializer.readHeader()#

讀取並驗證標頭(包括格式版本)。例如,可能會拒絕無效或不支援的連線格式。在這種情況下,會擲回 Error

deserializer.readValue()#

從緩衝區反序列化 JavaScript 值並傳回。

deserializer.transferArrayBuffer(id, arrayBuffer)#

標記一個 ArrayBuffer 作為其內容已傳輸出頻帶。將對應的 ArrayBuffer 傳遞到 serializer.transferArrayBuffer() 的序列化內容中(或在 SharedArrayBuffer 的情況下從 serializer._getSharedArrayBufferId() 傳回 id)。

deserializer.getWireFormatVersion()#

讀取底層線路格式版本。可能大多對讀取舊線路格式版本的舊式程式碼有用。不得在 .readHeader() 之前呼叫。

deserializer.readUint32()#

讀取一個原始 32 位元無符號整數並傳回。用於自訂 deserializer._readHostObject() 內部。

deserializer.readUint64()#

讀取一個未處理的 64 位元無符號整數,並以一個包含兩個 32 位元無符號整數項目的陣列 [hi, lo] 回傳。供自訂 deserializer._readHostObject() 內部使用。

deserializer.readDouble()#

讀取一個 JS number 值。供自訂 deserializer._readHostObject() 內部使用。

deserializer.readRawBytes(length)#

從 deserializer 的內部緩衝區讀取未處理的位元組。length 參數必須與傳遞給 serializer.writeRawBytes() 的緩衝區長度相符。供自訂 deserializer._readHostObject() 內部使用。

deserializer._readHostObject()#

呼叫此方法以讀取某種主機物件,也就是由原生 C++ 繫結建立的物件。如果無法將資料反序列化,應擲出適當的例外狀況。

此方法不存在於 Deserializer 類別本身,但可由子類別提供。

類別:v8.DefaultSerializer#

Serializer 的子類別,將 TypedArray(特別是 Buffer)和 DataView 物件序列化為主機物件,並只儲存它們所參照的底層 ArrayBuffers 的部分。

類別:v8.DefaultDeserializer#

對應於 DefaultSerializer 所寫入格式的 Deserializer 子類別。

Promise 鉤子#

promiseHooks 介面可用於追蹤 Promise 生命週期事件。若要追蹤所有非同步活動,請參閱 async_hooks,其在內部使用此模組來產生 Promise 生命週期事件,以及其他非同步資源的事件。對於要求內容管理,請參閱 AsyncLocalStorage

import { promiseHooks } from 'node:v8';

// There are four lifecycle events produced by promises:

// The `init` event represents the creation of a promise. This could be a
// direct creation such as with `new Promise(...)` or a continuation such
// as `then()` or `catch()`. It also happens whenever an async function is
// called or does an `await`. If a continuation promise is created, the
// `parent` will be the promise it is a continuation from.
function init(promise, parent) {
  console.log('a promise was created', { promise, parent });
}

// The `settled` event happens when a promise receives a resolution or
// rejection value. This may happen synchronously such as when using
// `Promise.resolve()` on non-promise input.
function settled(promise) {
  console.log('a promise resolved or rejected', { promise });
}

// The `before` event runs immediately before a `then()` or `catch()` handler
// runs or an `await` resumes execution.
function before(promise) {
  console.log('a promise is about to call a then handler', { promise });
}

// The `after` event runs immediately after a `then()` handler runs or when
// an `await` begins after resuming from another.
function after(promise) {
  console.log('a promise is done calling a then handler', { promise });
}

// Lifecycle hooks may be started and stopped individually
const stopWatchingInits = promiseHooks.onInit(init);
const stopWatchingSettleds = promiseHooks.onSettled(settled);
const stopWatchingBefores = promiseHooks.onBefore(before);
const stopWatchingAfters = promiseHooks.onAfter(after);

// Or they may be started and stopped in groups
const stopHookSet = promiseHooks.createHook({
  init,
  settled,
  before,
  after,
});

// To stop a hook, call the function returned at its creation.
stopWatchingInits();
stopWatchingSettleds();
stopWatchingBefores();
stopWatchingAfters();
stopHookSet(); 

promiseHooks.onInit(init)#

init 鉤子必須是純粹函式。提供非同步函式會擲回例外,因為它會產生無限微任務迴圈。

import { promiseHooks } from 'node:v8';

const stop = promiseHooks.onInit((promise, parent) => {});const { promiseHooks } = require('node:v8');

const stop = promiseHooks.onInit((promise, parent) => {});

promiseHooks.onSettled(settled)#

settled 鉤子必須是純粹函式。提供非同步函式會擲回例外,因為它會產生無限微任務迴圈。

import { promiseHooks } from 'node:v8';

const stop = promiseHooks.onSettled((promise) => {});const { promiseHooks } = require('node:v8');

const stop = promiseHooks.onSettled((promise) => {});

promiseHooks.onBefore(before)#

before 掛鉤必須是純函式。提供非同步函式會引發例外,因為它會產生無限微任務迴圈。

import { promiseHooks } from 'node:v8';

const stop = promiseHooks.onBefore((promise) => {});const { promiseHooks } = require('node:v8');

const stop = promiseHooks.onBefore((promise) => {});

promiseHooks.onAfter(after)#

after 掛鉤必須是純函式。提供非同步函式會引發例外,因為它會產生無限微任務迴圈。

import { promiseHooks } from 'node:v8';

const stop = promiseHooks.onAfter((promise) => {});const { promiseHooks } = require('node:v8');

const stop = promiseHooks.onAfter((promise) => {});

promiseHooks.createHook(callbacks)#

掛鉤回呼必須是純函式。提供非同步函式會引發例外,因為它會產生無限微任務迴圈。

註冊函式,以便在每個 Promise 的不同生命週期事件中呼叫。

呼叫回函 init()/before()/after()/settled() 會在承諾生命週期中的特定事件呼叫。

所有呼叫回函都是選用的。例如,如果只需要追蹤承諾建立,則只需要傳遞 init 呼叫回函。所有可以傳遞至 callbacks 的函數明細,請參閱 Hook Callbacks 區段。

import { promiseHooks } from 'node:v8';

const stopAll = promiseHooks.createHook({
  init(promise, parent) {},
});const { promiseHooks } = require('node:v8');

const stopAll = promiseHooks.createHook({
  init(promise, parent) {},
});

Hook Callbacks#

承諾生命週期中的主要事件已分類為四個區域:建立承諾、在呼叫延續處理常式之前/之後或在 await 周圍,以及承諾解析或拒絕時。

雖然這些 hook 類似於 async_hooks 的 hook,但它們缺少 destroy hook。其他類型的非同步資源通常代表具有明確「已關閉」狀態的 socket 或檔案描述符,以表達 destroy 生命週期事件,而承諾只要程式碼仍可存取它們,就能持續使用。垃圾回收追蹤用於使承諾符合 async_hooks 事件模型,但此追蹤非常昂貴,而且它們甚至不一定會被垃圾回收。

由於承諾是透過承諾 hook 機制追蹤其生命週期的非同步資源,因此 init()before()after()settled() 呼叫回函不得為非同步函數,因為它們會建立更多承諾,這將產生無限迴圈。

雖然此 API 用於將承諾事件提供給 async_hooks,但這兩個 API 之間的順序是未定義的。這兩個 API 都是多租戶的,因此可能會以相對於彼此的任何順序產生事件。

init(promise, parent)#
  • promise <Promise> 正在建立的 Promise。
  • parent <Promise> 繼續的 Promise(如果適用)。

在建立 Promise 時呼叫。這表示對應的 before/after 事件將會發生,只表示有這種可能性。如果建立 Promise 但從未繼續,就會發生這種情況。

before(promise)#

在 Promise 繼續執行之前呼叫。這可以是 then()catch()finally() 處理常式,或 await 繼續執行。

before 回呼會呼叫 0 到 N 次。如果 Promise 從未繼續執行,before 回呼通常會呼叫 0 次。如果從同一個 Promise 進行多次繼續執行,before 回呼可能會呼叫多次。

after(promise)#

在 Promise 繼續執行之後立即呼叫。這可能是 then()catch()finally() 處理常式之後,或在另一個 await 之後的 await 之前。

settled(promise)#

在 Promise 收到解決或拒絕值時呼叫。在 Promise.resolve()Promise.reject() 的情況下,這可能會同步發生。

啟動快照 API#

穩定性:1 - 實驗性

v8.startupSnapshot 介面可用於新增自訂啟動快照的序列化和反序列化掛勾。

$ node --snapshot-blob snapshot.blob --build-snapshot entry.js
# This launches a process with the snapshot
$ node --snapshot-blob snapshot.blob 

在上述範例中,entry.js 可以使用 v8.startupSnapshot 介面的方法來指定如何儲存自訂物件的資訊至序列化期間的快照,以及如何使用這些資訊來在快照反序列化的過程中同步這些物件。例如,如果 entry.js 包含以下指令碼

'use strict';

const fs = require('node:fs');
const zlib = require('node:zlib');
const path = require('node:path');
const assert = require('node:assert');

const v8 = require('node:v8');

class BookShelf {
  storage = new Map();

  // Reading a series of files from directory and store them into storage.
  constructor(directory, books) {
    for (const book of books) {
      this.storage.set(book, fs.readFileSync(path.join(directory, book)));
    }
  }

  static compressAll(shelf) {
    for (const [ book, content ] of shelf.storage) {
      shelf.storage.set(book, zlib.gzipSync(content));
    }
  }

  static decompressAll(shelf) {
    for (const [ book, content ] of shelf.storage) {
      shelf.storage.set(book, zlib.gunzipSync(content));
    }
  }
}

// __dirname here is where the snapshot script is placed
// during snapshot building time.
const shelf = new BookShelf(__dirname, [
  'book1.en_US.txt',
  'book1.es_ES.txt',
  'book2.zh_CN.txt',
]);

assert(v8.startupSnapshot.isBuildingSnapshot());
// On snapshot serialization, compress the books to reduce size.
v8.startupSnapshot.addSerializeCallback(BookShelf.compressAll, shelf);
// On snapshot deserialization, decompress the books.
v8.startupSnapshot.addDeserializeCallback(BookShelf.decompressAll, shelf);
v8.startupSnapshot.setDeserializeMainFunction((shelf) => {
  // process.env and process.argv are refreshed during snapshot
  // deserialization.
  const lang = process.env.BOOK_LANG || 'en_US';
  const book = process.argv[1];
  const name = `${book}.${lang}.txt`;
  console.log(shelf.storage.get(name));
}, shelf); 

結果的二進位檔將會在啟動期間列印從快照反序列化的資料,使用啟動的程序更新的 process.envprocess.argv

$ BOOK_LANG=es_ES node --snapshot-blob snapshot.blob book1
# Prints content of book1.es_ES.txt deserialized from the snapshot. 

目前從使用者端快照反序列化的應用程式無法再次建立快照,因此這些 API 僅對未從使用者端快照反序列化的應用程式可用。

v8.startupSnapshot.addSerializeCallback(callback[, data])#

  • callback <Function> 在序列化之前要呼叫的回呼。
  • data <any> 在呼叫 callback 時傳遞給 callback 的選用資料。

新增一個回呼,當 Node.js 執行個體即將序列化成快照並結束時,會呼叫該回呼。這可用於釋放不應或無法序列化的資源,或將使用者資料轉換成更適合序列化的格式。

回呼會依照新增順序執行。

v8.startupSnapshot.addDeserializeCallback(callback[, data])#

  • callback <Function> 在快照反序列化後呼叫的回呼函式。
  • data <any> 在呼叫 callback 時傳遞給 callback 的選用資料。

新增一個回呼函式,在 Node.js 執行個體從快照反序列化時呼叫。callbackdata(如果提供)會序列化到快照中,可使用它們重新初始化應用程式的狀態或重新取得應用程式在從快照重新啟動時需要的資源。

回呼會依照新增順序執行。

v8.startupSnapshot.setDeserializeMainFunction(callback[, data])#

  • callback <Function> 在快照反序列化後呼叫的回呼函式,做為進入點。
  • data <any> 在呼叫 callback 時傳遞給 callback 的選用資料。

這會設定 Node.js 應用程式在從快照反序列化時進入點。這只能在快照建置指令碼中呼叫一次。如果呼叫,反序列化的應用程式不再需要額外的進入點指令碼來啟動,而會直接呼叫回呼函式,並提供反序列化的資料(如果提供),否則仍需要提供進入點指令碼給反序列化的應用程式。

v8.startupSnapshot.isBuildingSnapshot()#

如果 Node.js 執行個體執行的是建置快照,則傳回 true。

類別:v8.GCProfiler#

此 API 會收集當前執行緒中的 GC 資料。

new v8.GCProfiler()#

建立 `v8.GCProfiler` 類別的新執行個體。

profiler.start()#

開始收集 GC 資料。

profiler.stop()#

停止收集 GC 資料並傳回一個物件。物件的內容如下。

{
  "version": 1,
  "startTime": 1674059033862,
  "statistics": [
    {
      "gcType": "Scavenge",
      "beforeGC": {
        "heapStatistics": {
          "totalHeapSize": 5005312,
          "totalHeapSizeExecutable": 524288,
          "totalPhysicalSize": 5226496,
          "totalAvailableSize": 4341325216,
          "totalGlobalHandlesSize": 8192,
          "usedGlobalHandlesSize": 2112,
          "usedHeapSize": 4883840,
          "heapSizeLimit": 4345298944,
          "mallocedMemory": 254128,
          "externalMemory": 225138,
          "peakMallocedMemory": 181760
        },
        "heapSpaceStatistics": [
          {
            "spaceName": "read_only_space",
            "spaceSize": 0,
            "spaceUsedSize": 0,
            "spaceAvailableSize": 0,
            "physicalSpaceSize": 0
          }
        ]
      },
      "cost": 1574.14,
      "afterGC": {
        "heapStatistics": {
          "totalHeapSize": 6053888,
          "totalHeapSizeExecutable": 524288,
          "totalPhysicalSize": 5500928,
          "totalAvailableSize": 4341101384,
          "totalGlobalHandlesSize": 8192,
          "usedGlobalHandlesSize": 2112,
          "usedHeapSize": 4059096,
          "heapSizeLimit": 4345298944,
          "mallocedMemory": 254128,
          "externalMemory": 225138,
          "peakMallocedMemory": 181760
        },
        "heapSpaceStatistics": [
          {
            "spaceName": "read_only_space",
            "spaceSize": 0,
            "spaceUsedSize": 0,
            "spaceAvailableSize": 0,
            "physicalSpaceSize": 0
          }
        ]
      }
    }
  ],
  "endTime": 1674059036865
} 

以下是一個範例。

const { GCProfiler } = require('v8');
const profiler = new GCProfiler();
profiler.start();
setTimeout(() => {
  console.log(profiler.stop());
}, 1000);