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 ヒープスナップショットを含む Readable。

現在の V8 ヒープのスナップショットを生成し、JSON シリアライズされた表現を読み取るために使用できる Readable ストリームを返します。この JSON ストリーム形式は、Chrome DevTools などのツールで使用することを目的としています。JSON スキーマは文書化されておらず、V8 エンジンに固有です。したがって、スキーマは V8 のバージョンごとに変更される可能性があります。

ヒープスナップショットの作成には、スナップショット作成時のヒープサイズの約2倍のメモリが必要です。これにより、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()#

以下のプロパティを持つオブジェクトを返します。

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 の値は、ArrayBuffer と外部文字列のメモリサイズです。

{
  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 - Active development

  • 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 コマンドラインフラグを設定するために使用できます。このメソッドは注意して使用する必要があります。VM が起動した後に設定を変更すると、クラッシュやデータ損失を含む予測不能な動作を引き起こす可能性があります。または、単に何も起こらない場合もあります。

Node.js のバージョンで利用可能な V8 オプションは、node --v8-options を実行することで確認できます。

使用法

// 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}writeHeapSnapshot() がメインの Node.js スレッドから呼び出された場合は 0、ワーカースレッドの ID になります。
  • options <Object>
    • exposeInternals <boolean> true の場合、ヒープスナップショットに内部情報を公開します。デフォルト: false
    • exposeNumericValues <boolean> true の場合、人工的なフィールドに数値を公開します。デフォルト: false
  • 戻り値: <string> スナップショットが保存されたファイル名。

現在の V8 ヒープのスナップショットを生成し、JSON ファイルに書き込みます。このファイルは Chrome DevTools などのツールで使用することを目的としています。JSON スキーマは文書化されておらず、V8 エンジンに固有であり、V8 のバージョンごとに変更される可能性があります。

ヒープスナップショットは単一の V8 アイソレートに固有です。ワーカースレッドを使用する場合、メインスレッドから生成されたヒープスナップショットにはワーカーに関する情報は含まれず、その逆も同様です。

ヒープスナップショットの作成には、スナップショット作成時のヒープサイズの約2倍のメモリが必要です。これにより、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)#

--heapsnapshot-near-heap-limit がコマンドラインからすでに設定されている場合、または API が複数回呼び出された場合、この API は何もしません。limit は正の整数でなければなりません。詳細については、--heapsnapshot-near-heap-limit を参照してください。

シリアライゼーション API#

シリアライゼーション API は、HTML 構造化クローンアルゴリズムと互換性のある方法で JavaScript の値をシリアライズする手段を提供します。

形式は下位互換性があります(つまり、ディスクに保存しても安全です)。同じ JavaScript の値でも、シリアライズされた出力が異なる場合があります。

v8.serialize(value)#

DefaultSerializer を使用して value をバッファにシリアライズします。

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 の内容が帯域外で転送されたものとしてマークします。デシリアライズコンテキストで対応する ArrayBufferdeserializer.transferArrayBuffer() に渡します。

serializer.writeUint32(value)#

生の32ビット符号なし整数を書き込みます。カスタムの serializer._writeHostObject() 内で使用します。

serializer.writeUint64(hi, lo)#

生の64ビット符号なし整数を、上位と下位の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)#

TypedArray および DataView オブジェクトをホストオブジェクトとして扱うかどうかを示します。つまり、それらを serializer._writeHostObject() に渡します。

クラス: v8.Deserializer#

new Deserializer(buffer)#

新しい Deserializer オブジェクトを作成します。

deserializer.readHeader()#

ヘッダー(形式バージョンを含む)を読み取り、検証します。たとえば、無効またはサポートされていないワイヤフォーマットを拒否する場合があります。その場合、Error がスローされます。

deserializer.readValue()#

バッファから JavaScript の値をデシリアライズして返します。

deserializer.transferArrayBuffer(id, arrayBuffer)#

ArrayBuffer の内容が帯域外で転送されたものとしてマークします。シリアライズコンテキストで対応する ArrayBufferserializer.transferArrayBuffer() に渡します(または SharedArrayBuffer の場合は serializer._getSharedArrayBufferId() から id を返します)。

deserializer.getWireFormatVersion()#

基礎となるワイヤフォーマットのバージョンを読み取ります。古いワイヤフォーマットバージョンを読み取るレガシーコードに主に役立つでしょう。.readHeader() の前に呼び出すことはできません。

deserializer.readUint32()#

生の32ビット符号なし整数を読み取り、それを返します。カスタムの deserializer._readHostObject() 内で使用します。

deserializer.readUint64()#

生の64ビット符号なし整数を読み取り、2つの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 が解決または拒否されるときの4つの領域に分類されています。

これらのフックは async_hooks のものと似ていますが、destroy フックがありません。他のタイプの非同期リソースは通常、destroy ライフサイクルイベントを表現するための明確な「クローズ」状態を持つソケットやファイル記述子を表しますが、Promise はコードがまだ到達できる限り使用可能なままです。ガベージコレクションの追跡は、Promise を async_hooks のイベントモデルに適合させるために使用されますが、この追跡は非常にコストがかかり、必ずしもガベージコレクションされるとは限りません。

Promise は Promise フックメカニズムを介してライフサイクルが追跡される非同期リソースであるため、init()before()after()、および settled() コールバックは非同期関数であってはなりません。なぜなら、それらはさらに多くの Promise を作成し、無限ループを引き起こすからです。

この API は async_hooks に Promise イベントを供給するために使用されますが、2つの間の順序は未定義です。両方の 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.jsv8.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 を返します。

cpuProfileHandle[Symbol.asyncDispose]()#

プロファイルの収集を停止し、プロファイルは破棄されます。

クラス: HeapProfileHandle#

heapProfileHandle.stop()#

プロファイルの収集を停止し、エラーまたはプロファイルデータで解決される 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);