V8#

原始碼: lib/v8.js

node:v8 模組暴露了特定於 Node.js 二進位制檔案中內建的 V8 版本的 API。可以使用以下方式訪問:

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

v8.cachedDataVersionTag()#

返回一個整數,表示從 V8 版本、命令列標誌和檢測到的 CPU 特性派生出的版本標籤。這對於確定 vm.ScriptcachedData 緩衝區是否與此 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 堆快照的可讀流。

生成當前 V8 堆的快照,並返回一個可讀流,可用於讀取 JSON 序列化表示。此 JSON 流格式旨在與 Chrome DevTools 等工具一起使用。該 JSON 模式未被文件化,且特定於 V8 引擎。因此,該模式可能會隨著 V8 版本的不同而改變。

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

生成快照是一個同步操作,它會阻塞事件迴圈,持續時間取決於堆的大小。

// 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()#

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

total_heap_size 的值是 V8 為堆分配的位元組數。如果 used_heap 需要更多記憶體,這個值可能會增長。

total_heap_size_executable 的值是堆中可以包含可執行程式碼的部分,以位元組為單位。這包括 JIT 編譯程式碼使用的記憶體以及任何必須保持可執行狀態的記憶體。

total_physical_size 的值是 V8 堆實際使用的物理記憶體,以位元組為單位。這是已提交(或正在使用)而不是預留的記憶體量。

total_available_size 的值是 V8 堆可用的記憶體位元組數。這個值表示 V8 在超過堆限制之前還可以使用多少記憶體。

used_heap_size 的值是 V8 的 JavaScript 物件當前正在使用的位元組數。這是實際使用的記憶體,不包括已分配但尚未使用​​的記憶體。

heap_size_limit 的值是 V8 堆的最大大小,以位元組為單位(可以是預設限制,由系統資源決定,也可以是傳遞給 --max_old_space_size 選項的值)。

malloced_memory 的值是 V8 透過 malloc 分配的位元組數。

peak_malloced_memory 的值是程序生命週期內 V8 透過 malloc 分配的峰值位元組數。

does_zap_garbage 是一個 0/1 的布林值,表示 --zap_code_space 選項是否啟用。這使得 V8 用一個位模式覆蓋堆中的垃圾。RSS(駐留集大小)佔用空間會變大,因為它持續地接觸所有堆頁面,這使得它們不太可能被作業系統換出。

number_of_native_contexts 的值是當前活動的最頂層上下文的數量。這個數字隨時間增加表明存在記憶體洩漏。

number_of_detached_contexts 的值是已分離但尚未被垃圾回收的上下文數量。這個數字非零表明可能存在記憶體洩漏。

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

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

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.getCppHeapStatistics([detailLevel])#

使用 V8 的 CollectStatistics() 函式檢索關於記憶體消耗和利用率的 CppHeap 統計資訊,該函式可能隨 V8 版本的不同而改變。

  • detailLevel <string> | <undefined>預設值:'detailed'。指定返回統計資訊的詳細級別。可接受的值為:
    • 'brief':簡要統計資訊只包含整個堆的頂層已分配和已使用記憶體統計。
    • 'detailed':詳細統計資訊還包含按空間和頁面的細分,以及空閒列表統計和物件型別直方圖。

它返回一個結構與 cppgc::HeapStatistics 物件類似的物件。有關該物件屬性的更多資訊,請參閱 V8 文件

// Detailed
({
  committed_size_bytes: 131072,
  resident_size_bytes: 131072,
  used_size_bytes: 152,
  space_statistics: [
    {
      name: 'NormalPageSpace0',
      committed_size_bytes: 0,
      resident_size_bytes: 0,
      used_size_bytes: 0,
      page_stats: [{}],
      free_list_stats: {},
    },
    {
      name: 'NormalPageSpace1',
      committed_size_bytes: 131072,
      resident_size_bytes: 131072,
      used_size_bytes: 152,
      page_stats: [{}],
      free_list_stats: {},
    },
    {
      name: 'NormalPageSpace2',
      committed_size_bytes: 0,
      resident_size_bytes: 0,
      used_size_bytes: 0,
      page_stats: [{}],
      free_list_stats: {},
    },
    {
      name: 'NormalPageSpace3',
      committed_size_bytes: 0,
      resident_size_bytes: 0,
      used_size_bytes: 0,
      page_stats: [{}],
      free_list_stats: {},
    },
    {
      name: 'LargePageSpace',
      committed_size_bytes: 0,
      resident_size_bytes: 0,
      used_size_bytes: 0,
      page_stats: [{}],
      free_list_stats: {},
    },
  ],
  type_names: [],
  detail_level: 'detailed',
}); 
// Brief
({
  committed_size_bytes: 131072,
  resident_size_bytes: 131072,
  used_size_bytes: 128864,
  space_statistics: [],
  type_names: [],
  detail_level: 'brief',
}); 

v8.queryObjects(ctor[, options])#

穩定性:1.1 - 活躍開發

  • ctor <Function> 可用於在原型鏈上搜索以過濾堆中目標物件的建構函式。
  • options <undefined> | <Object>
    • format <string> 如果是 'count',則返回匹配物件的數量。如果是 'summary',則返回一個包含匹配物件摘要字串的陣列。
  • 返回:{number|Array}

這類似於 Chromium DevTools 控制檯提供的 queryObjects() 控制檯 API。它可以在一次完整的垃圾回收後,用於在堆中搜索在其原型鏈上具有匹配建構函式的物件,這對於記憶體洩漏迴歸測試非常有用。為避免意外結果,使用者應避免在他們無法控制其實現的建構函式上使用此 API,或在應用程式中可能被其他方呼叫的建構函式上使用。

為了避免意外洩漏,此 API 不會返回找到物件的原始引用。預設情況下,它返回找到的物件的數量。如果 options.format'summary',它會返回一個包含每個物件的簡短字串表示的陣列。此 API 中提供的可見性類似於堆快照提供的可見性,但使用者可以節省序列化和解析的成本,並直接在搜尋過程中過濾目標物件。

只有在當前執行上下文中建立的物件才會包含在結果中。

const { queryObjects } = require('node:v8');
class A { foo = 'bar'; }
console.log(queryObjects(A)); // 0
const a = new A();
console.log(queryObjects(A)); // 1
// [ "A { foo: 'bar' }" ]
console.log(queryObjects(A, { format: 'summary' }));

class B extends A { bar = 'qux'; }
const b = new B();
console.log(queryObjects(B)); // 1
// [ "B { foo: 'bar', bar: 'qux' }" ]
console.log(queryObjects(B, { format: 'summary' }));

// Note that, when there are child classes inheriting from a constructor,
// the constructor also shows up in the prototype chain of the child
// classes's prototype, so the child classes's prototype would also be
// included in the result.
console.log(queryObjects(A));  // 3
// [ "B { foo: 'bar', bar: 'qux' }", 'A {}', "A { foo: 'bar' }" ]
console.log(queryObjects(A, { format: 'summary' }));import { queryObjects } from 'node:v8';
class A { foo = 'bar'; }
console.log(queryObjects(A)); // 0
const a = new A();
console.log(queryObjects(A)); // 1
// [ "A { foo: 'bar' }" ]
console.log(queryObjects(A, { format: 'summary' }));

class B extends A { bar = 'qux'; }
const b = new B();
console.log(queryObjects(B)); // 1
// [ "B { foo: 'bar', bar: 'qux' }" ]
console.log(queryObjects(B, { format: 'summary' }));

// Note that, when there are child classes inheriting from a constructor,
// the constructor also shows up in the prototype chain of the child
// classes's prototype, so the child classes's prototype would also be
// included in the result.
console.log(queryObjects(A));  // 3
// [ "B { foo: 'bar', bar: 'qux' }", 'A {}', "A { foo: 'bar' }" ]
console.log(queryObjects(A, { format: 'summary' }));

v8.setFlagsFromString(flags)#

v8.setFlagsFromString() 方法可用於以程式設計方式設定 V8 命令列標誌。應謹慎使用此方法。在虛擬機器啟動後更改設定可能會導致不可預測的行為,包括崩潰和資料丟失;或者它可能根本不起作用。

可以透過執行 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} 在從主 Node.js 執行緒呼叫 writeHeapSnapshot() 時為 0,或者是工作執行緒的 ID。
  • options <Object>
    • exposeInternals <boolean> 如果為 true,則在堆快照中暴露內部資訊。預設值: false
    • exposeNumericValues <boolean> 如果為 true,則在人工欄位中暴露數值。預設值: false
  • 返回:<string> 儲存快照的檔名。

生成當前 V8 堆的快照並將其寫入一個 JSON 檔案。此檔案旨在與 Chrome DevTools 等工具一起使用。該 JSON 模式未被文件化,且特定於 V8 引擎,並可能隨著 V8 版本的不同而改變。

堆快照特定於單個 V8 isolate。當使用 工作執行緒時,從主執行緒生成的堆快照將不包含有關工作執行緒的任何資訊,反之亦然。

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

生成快照是一個同步操作,它會阻塞事件迴圈,持續時間取決於堆的大小。

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)#

如果已從命令列設定了 --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 位和低 32 位部分。用於自定義的 serializer._writeHostObject() 內部。

serializer.writeDouble(value)#

寫入一個 JS number 值。用於自定義的 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)#

從反序列化器的內部緩衝區讀取原始位元組。length 引數必須對應於傳遞給 serializer.writeRawBytes() 的緩衝區的長度。用於自定義的 deserializer._readHostObject() 內部。

deserializer._readHostObject()#

此方法用於讀取某種宿主物件,即由原生 C++ 繫結建立的物件。如果無法反序列化資料,則應丟擲適當的異常。

此方法不在 Deserializer 類本身上,但可以由子類提供。

類:v8.DefaultSerializer#

Serializer 的一個子類,它將 TypedArray(特別是 Buffer)和 DataView 物件作為宿主物件進行序列化,並且只儲存它們所引用的底層 ArrayBuffer 的部分。

類:v8.DefaultDeserializer#

Deserializer 的一個子類,對應於由 DefaultSerializer 寫入的格式。

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() 會在 promise 生命週期中相應的事件發生時被呼叫。

所有回撥都是可選的。例如,如果只需要跟蹤 promise 的建立,那麼只需要傳遞 init 回撥。所有可以傳遞給 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) {},
});

鉤子回撥#

一個 promise 生命週期中的關鍵事件被分為四個方面:promise 的建立、延續處理程式呼叫之前/之後或圍繞 await,以及 promise 解決或拒絕時。

雖然這些鉤子與 async_hooks 的鉤子相似,但它們缺少一個 destroy 鉤子。其他型別的非同步資源通常代表套接字或檔案描述符,它們有一個明確的“關閉”狀態來表示 destroy 生命週期事件,而 promise 只要程式碼還能訪問到它們就一直可用。垃圾回收跟蹤被用來使 promise 適應 async_hooks 事件模型,但是這種跟蹤非常昂貴,而且它們甚至可能永遠不會被垃圾回收。

因為 promise 是透過 promise 鉤子機制跟蹤其生命週期的非同步資源,所以 init()before()after()settled() 回撥*不能*是非同步函式,因為它們會建立更多的 promise,從而產生無限迴圈。

雖然此 API 用於將 promise 事件提供給 async_hooks,但兩者之間的順序是未定義的。這兩個 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#

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 時將傳遞給它。

新增一個回撥函式,當 Node.js 例項即將被序列化成快照並退出時呼叫。這可以用於釋放不應或無法序列化的資源,或將使用者資料轉換為更適合序列化的形式。

回撥函式按其新增的順序執行。

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

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

新增一個回撥函式,當 Node.js 例項從快照反序列化時呼叫。callbackdata(如果提供)將被序列化到快照中,它們可用於重新初始化應用程式的狀態,或在應用程式從快照重啟時重新獲取其所需的資源。

回撥函式按其新增的順序執行。

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

  • callback <Function> 在快照反序列化後作為入口點呼叫的回撥函式。
  • data <any> 可選資料,在呼叫 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('node:v8');
const profiler = new GCProfiler();
profiler.start();
setTimeout(() => {
  console.log(profiler.stop());
}, 1000); 

類:SyncCPUProfileHandle#

syncCpuProfileHandle.stop()#

停止收集分析資料並返回分析資料。

syncCpuProfileHandle[Symbol.dispose]()#

停止收集分析資料,並且分析資料將被丟棄。

類:CPUProfileHandle#

cpuProfileHandle.stop()#

停止收集分析資料,然後返回一個 Promise,該 Promise 會以錯誤或分析資料來履行。

cpuProfileHandle[Symbol.asyncDispose]()#

停止收集分析資料,並且分析資料將被丟棄。

類:HeapProfileHandle#

heapProfileHandle.stop()#

停止收集分析資料,然後返回一個 Promise,該 Promise 會以錯誤或分析資料來履行。

heapProfileHandle[Symbol.asyncDispose]()#

停止收集分析資料,並且分析資料將被丟棄。

v8.isStringOneByteRepresentation(content)#

V8 只支援 Latin-1/ISO-8859-1UTF16 作為字串的底層表示。如果 content 使用 Latin-1/ISO-8859-1 作為底層表示,此函式將返回 true;否則,返回 false。

如果此方法返回 false,並不意味著該字串包含一些不在 Latin-1/ISO-8859-1 中的字元。有時一個 Latin-1 字串也可能被表示為 UTF16

const { isStringOneByteRepresentation } = require('node:v8');

const Encoding = {
  latin1: 1,
  utf16le: 2,
};
const buffer = Buffer.alloc(100);
function writeString(input) {
  if (isStringOneByteRepresentation(input)) {
    buffer.writeUint8(Encoding.latin1);
    buffer.writeUint32LE(input.length, 1);
    buffer.write(input, 5, 'latin1');
  } else {
    buffer.writeUint8(Encoding.utf16le);
    buffer.writeUint32LE(input.length * 2, 1);
    buffer.write(input, 5, 'utf16le');
  }
}
writeString('hello');
writeString('你好'); 

v8.startCpuProfile()#

啟動 CPU 分析,然後返回一個 SyncCPUProfileHandle 物件。此 API 支援 using 語法。

const handle = v8.startCpuProfile();
const profile = handle.stop();
console.log(profile);