Node.js v20.11.0 文档


V8#

源代码: lib/v8.js

node:v8 模块公开了特定于 Node.js 二进制文件中内置的 V8 版本的 API。可以使用以下方式访问它:

The node:v8 module exposes APIs that are specific to the version of V8 built into the Node.js binary. It can be accessed using:

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

v8.cachedDataVersionTag()#

返回表示从 V8 版本、命令行标志、以及检测到的 CPU 特性派生的版本标签的整数。这对于判断 vm.Script cachedData 缓冲区是否与此 V8 实例兼容很有用。

Returns an integer representing a version tag derived from the V8 version, command-line flags, and detected CPU features. This is useful for determining whether a vm.Script cachedData buffer is compatible with this instance of 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。返回具有以下属性的对象:

Get statistics about code and its metadata in the heap, see V8 GetHeapCodeAndMetadataStatistics API. Returns an object with the following properties:

{
  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> 如果为真,则在堆快照中公开内部结构。默认值:false

      exposeInternals <boolean> If true, expose internals in the heap snapshot. Default: false.

    • exposeNumericValues <boolean> 如果为真,则在人工字段中公开数值。默认值:false

      exposeNumericValues <boolean> If true, expose numeric values in artificial fields. Default: false.

  • 返回:<stream.Readable> 包含 V8 堆快照的可读对象。

    Returns: <stream.Readable> A Readable containing the V8 heap snapshot.

生成当前 V8 堆的快照并返回可用于读取 JSON 序列化表示的可读流。此 JSON 流格式旨在与 Chrome 开发者工具等工具一起使用。JSON 模式未记录并且特定于 V8 引擎。因此,模式可能会从 V8 的一个版本更改为下一个版本。

Generates a snapshot of the current V8 heap and returns a Readable Stream that may be used to read the JSON serialized representation. This JSON stream format is intended to be used with tools such as Chrome DevTools. The JSON schema is undocumented and specific to the V8 engine. Therefore, the schema may change from one version of V8 to the next.

创建堆快照需要的内存大约是创建快照时堆大小的两倍。这会导致 OOM 杀手终止进程的风险。

Creating a heap snapshot requires memory about twice the size of the heap at the time the snapshot is created. This results in the risk of OOM killers terminating the process.

生成快照是一个同步的操作,它会根据堆大小在一段时间内阻塞事件循环。

Generating a snapshot is a synchronous operation which blocks the event loop for a duration depending on the heap size.

// 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 版本更改为下一个版本。

Returns statistics about the V8 heap spaces, i.e. the segments which make up the V8 heap. Neither the ordering of heap spaces, nor the availability of a heap space can be guaranteed as the statistics are provided via the V8 GetHeapSpaceStatistics function and may change from one V8 version to the next.

返回的值是包含以下属性的对象数组

The value returned is an array of objects containing the following properties:

[
  {
    "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()#

返回具有以下属性的对象:

Returns an object with the following properties:

does_zap_garbage 是 0/​​1 布尔值,表示是否启用了 --zap_code_space 选项。这使得 V8 使用位模式覆盖堆垃圾。RSS 占用空间(常驻集大小)变得更大,因为它不断接触所有堆页面,这使得它们不太可能被操作系统换出。

does_zap_garbage is a 0/1 boolean, which signifies whether the --zap_code_space option is enabled or not. This makes V8 overwrite heap garbage with a bit pattern. The RSS footprint (resident set size) gets bigger because it continuously touches all heap pages and that makes them less likely to get swapped out by the operating system.

number_of_native_contexts native_context 的值是当前活动的顶层上下文的数量。随着时间的推移此数字的增加表示内存泄漏。

number_of_native_contexts The value of native_context is the number of the top-level contexts currently active. Increase of this number over time indicates a memory leak.

number_of_detached_contexts detached_context 的值是已分离但尚未进行垃圾收集的上下文的数量。此数字非零表示潜在的内存泄漏。

number_of_detached_contexts The value of detached_context is the number of contexts that were detached and not yet garbage collected. This number being non-zero indicates a potential memory leak.

total_global_handles_size Total_global_handles_size 的值是 V8 全局句柄的总内存大小。

total_global_handles_size The value of total_global_handles_size is the total memory size of V8 global handles.

used_global_handles_size used_global_handles_size 的值是 V8 全局句柄的已用内存大小。

used_global_handles_size The value of used_global_handles_size is the used memory size of V8 global handles.

external_memory external_memory 的值是数组缓冲区和外部字符串的内存大小。

external_memory The value of external_memory is the memory size of array buffers and external strings.

{
  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 启动后更改设置可能会导致不可预测的行为,包括崩溃和数据丢失;或者它可能什么都不做。

The v8.setFlagsFromString() method can be used to programmatically set V8 command-line flags. This method should be used with care. Changing settings after the VM has started may result in unpredictable behavior, including crashes and data loss; or it may simply do nothing.

可以通过运行 node --v8-options 来确定 Node.js 版本可用的 V8 选项。

The V8 options available for a version of Node.js may be determined by running node --v8-options.

用法:

Usage:

// 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() 结合使用。

The v8.stopCoverage() method allows the user to stop the coverage collection started by NODE_V8_COVERAGE, so that V8 can release the execution count records and optimize code. This can be used in conjunction with v8.takeCoverage() if the user wants to collect the coverage on demand.

v8.takeCoverage()#

v8.takeCoverage() 方法允许用户按需将 NODE_V8_COVERAGE 开始的覆盖写入磁盘。此方法可以在进程的生命周期内多次调用。每次执行计数器将被重置,并且新的覆盖报告将写入 NODE_V8_COVERAGE 指定的目录。

The v8.takeCoverage() method allows the user to write the coverage started by NODE_V8_COVERAGE to disk on demand. This method can be invoked multiple times during the lifetime of the process. Each time the execution counter will be reset and a new coverage report will be written to the directory specified by NODE_V8_COVERAGE.

当进程即将退出时,除非在进程退出前调用 v8.stopCoverage(),否则最后一个覆盖仍会写入磁盘。

When the process is about to exit, one last coverage will still be written to disk unless v8.stopCoverage() is invoked before the process exits.

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

  • filename <string> 要保存 V8 堆快照的文件路径。如果不指定,则会生成格式为 'Heap-${yyyymmdd}-${hhmmss}-${pid}-${thread_id}.heapsnapshot' 的文件名,其中 {pid} 是 Node.js 进程的 PID,当 writeHeapSnapshot() 从 Node.js 主线程调用时,{thread_id} 将是 0 或工作线程的 id。

    filename <string> The file path where the V8 heap snapshot is to be saved. If not specified, a file name with the pattern 'Heap-${yyyymmdd}-${hhmmss}-${pid}-${thread_id}.heapsnapshot' will be generated, where {pid} will be the PID of the Node.js process, {thread_id} will be 0 when writeHeapSnapshot() is called from the main Node.js thread or the id of a worker thread.

  • options <Object>

    • exposeInternals <boolean> 如果为真,则在堆快照中公开内部结构。默认值:false

      exposeInternals <boolean> If true, expose internals in the heap snapshot. Default: false.

    • exposeNumericValues <boolean> 如果为真,则在人工字段中公开数值。默认值:false

      exposeNumericValues <boolean> If true, expose numeric values in artificial fields. Default: false.

  • 返回:<string> 保存快照的文件名。

    Returns: <string> The filename where the snapshot was saved.

生成当前 V8 堆的快照并将其写入 JSON 文件。此文件旨在与 Chrome 开发者工具等工具一起使用JSON 模式未记录并且特定于 V8 引擎,并且可能会从 V8 的一个版本更改为下一个版本。

Generates a snapshot of the current V8 heap and writes it to a JSON file. This file is intended to be used with tools such as Chrome DevTools. The JSON schema is undocumented and specific to the V8 engine, and may change from one version of V8 to the next.

堆快照特定于单个 V8 隔离。使用 工作线程 时,从主线程生成的堆快照将不包含有关工作进程的任何信息,反之亦然。

A heap snapshot is specific to a single V8 isolate. When using worker threads, a heap snapshot generated from the main thread will not contain any information about the workers, and vice versa.

创建堆快照需要的内存大约是创建快照时堆大小的两倍。这会导致 OOM 杀手终止进程的风险。

Creating a heap snapshot requires memory about twice the size of the heap at the time the snapshot is created. This results in the risk of OOM killers terminating the process.

生成快照是一个同步的操作,它会根据堆大小在一段时间内阻塞事件循环。

Generating a snapshot is a synchronous operation which blocks the event loop for a duration depending on the heap size.

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 - 实验性的

Stability: 1 - Experimental

如果 --heapsnapshot-near-heap-limit 已经从命令行设置或 API 被多次调用,则 API 是无操作的。limit 必须是正整数。有关详细信息,请参阅 --heapsnapshot-near-heap-limit

The API is a no-op if --heapsnapshot-near-heap-limit is already set from the command line or the API is called more than once. limit must be a positive integer. See --heapsnapshot-near-heap-limit for more information.

序列化 API#

Serialization API

序列化 API 提供了以与 HTML 结构化克隆算法 兼容的方式序列化 JavaScript 值的方法。

The serialization API provides means of serializing JavaScript values in a way that is compatible with the HTML structured clone algorithm.

格式是向后兼容的(即可以安全地存储到磁盘)。相同的 JavaScript 值可能会导致不同的序列化输出。

The format is backward-compatible (i.e. safe to store to disk). Equal JavaScript values may result in different serialized output.

v8.serialize(value)#

使用 DefaultSerializervalue 序列化到缓冲区中。

Uses a DefaultSerializer to serialize value into a buffer.

尝试序列化需要大于 buffer.constants.MAX_LENGTH 的缓冲区的大对象时,则将抛出 ERR_BUFFER_TOO_LARGE

ERR_BUFFER_TOO_LARGE will be thrown when trying to serialize a huge object which requires buffer larger than buffer.constants.MAX_LENGTH.

v8.deserialize(buffer)#

使用带有默认选项的 DefaultDeserializer 从缓冲区读取 JS 值。

Uses a DefaultDeserializer with default options to read a JS value from a buffer.

类:v8.Serializer#

Class: v8.Serializer

new Serializer()#

创建新的 Serializer 对象。

Creates a new Serializer object.

serializer.writeHeader()#

写出标头,其中包括序列化格式版本。

Writes out a header, which includes the serialization format version.

serializer.writeValue(value)#

序列化 JavaScript 值并将序列化的表示添加到内部缓冲区。

Serializes a JavaScript value and adds the serialized representation to the internal buffer.

如果无法序列化 value,则抛出错误。

This throws an error if value cannot be serialized.

serializer.releaseBuffer()#

返回存储的内部缓冲区。释放缓冲区后不应使用此序列化器。如果先前的写入失败,则调用此方法会导致未定义的行为。

Returns the stored internal buffer. This serializer should not be used once the buffer is released. Calling this method results in undefined behavior if a previous write has failed.

serializer.transferArrayBuffer(id, arrayBuffer)#

ArrayBuffer 标记为将其内容传输到带外。将反序列化上下文中对应的 ArrayBuffer 传给 deserializer.transferArrayBuffer()

Marks an ArrayBuffer as having its contents transferred out of band. Pass the corresponding ArrayBuffer in the deserializing context to deserializer.transferArrayBuffer().

serializer.writeUint32(value)#

写入原始的 32 位无符号整数。用于自定义的 serializer._writeHostObject() 内部。

Write a raw 32-bit unsigned integer. For use inside of a custom serializer._writeHostObject().

serializer.writeUint64(hi, lo)#

写入原始的 64 位无符号整数,分成高和低 32 位部分。用于自定义的 serializer._writeHostObject() 内部。

Write a raw 64-bit unsigned integer, split into high and low 32-bit parts. For use inside of a custom serializer._writeHostObject().

serializer.writeDouble(value)#

写入 JS number 值。用于自定义的 serializer._writeHostObject() 内部。

Write a JS number value. For use inside of a custom serializer._writeHostObject().

serializer.writeRawBytes(buffer)#

将原始字节写入序列化器的内部缓冲区。反序列化器需要一种方法来计算缓冲区的长度。用于自定义的 serializer._writeHostObject() 内部。

Write raw bytes into the serializer's internal buffer. The deserializer will require a way to compute the length of the buffer. For use inside of a custom serializer._writeHostObject().

serializer._writeHostObject(object)#

调用此方法来写入某种宿主对象,即由原生 C++ 绑定创建的对象。如果无法序列化 object,则应抛出合适的异常。

This method is called to write some kind of host object, i.e. an object created by native C++ bindings. If it is not possible to serialize object, a suitable exception should be thrown.

此方法不存在于 Serializer 类本身,但可以由子类提供。

This method is not present on the Serializer class itself but can be provided by subclasses.

serializer._getDataCloneError(message)#

调用此方法生成错误对象,当无法克隆对象时将抛出该错误对象。

This method is called to generate error objects that will be thrown when an object can not be cloned.

此方法默认为 Error 构造函数,并且可以在子类上覆盖。

This method defaults to the Error constructor and can be overridden on subclasses.

serializer._getSharedArrayBufferId(sharedArrayBuffer)#

此方法在序列化器要序列化 SharedArrayBuffer 对象时被调用。它必须为对象返回无符号的 32 位整数 ID,如果此 SharedArrayBuffer 已被序列化,则使用相同的 ID。当反序列化时,此 ID 会传给 deserializer.transferArrayBuffer()

This method is called when the serializer is going to serialize a SharedArrayBuffer object. It must return an unsigned 32-bit integer ID for the object, using the same ID if this SharedArrayBuffer has already been serialized. When deserializing, this ID will be passed to deserializer.transferArrayBuffer().

如果对象无法序列化,则应抛出异常。

If the object cannot be serialized, an exception should be thrown.

此方法不存在于 Serializer 类本身,但可以由子类提供。

This method is not present on the Serializer class itself but can be provided by subclasses.

serializer._setTreatArrayBufferViewsAsHostObjects(flag)#

指示是否将 TypedArrayDataView 对象视为宿主对象,即将它们传给 serializer._writeHostObject()

Indicate whether to treat TypedArray and DataView objects as host objects, i.e. pass them to serializer._writeHostObject().

类:v8.Deserializer#

Class: v8.Deserializer

new Deserializer(buffer)#

创建新的 Deserializer 对象。

Creates a new Deserializer object.

deserializer.readHeader()#

读取并验证标头(包括格式版本)。例如,可以拒绝无效或不受支持的有线格式。在这种情况下,会抛出 Error

Reads and validates a header (including the format version). May, for example, reject an invalid or unsupported wire format. In that case, an Error is thrown.

deserializer.readValue()#

从缓冲区反序列化 JavaScript 值并返回。

Deserializes a JavaScript value from the buffer and returns it.

deserializer.transferArrayBuffer(id, arrayBuffer)#

ArrayBuffer 标记为将其内容传输到带外。将序列化上下文中对应的 ArrayBuffer 传递给 serializer.transferArrayBuffer()(或者在 SharedArrayBuffer 的情况下从 serializer._getSharedArrayBufferId() 返回 id)。

Marks an ArrayBuffer as having its contents transferred out of band. Pass the corresponding ArrayBuffer in the serializing context to serializer.transferArrayBuffer() (or return the id from serializer._getSharedArrayBufferId() in the case of SharedArrayBuffers).

deserializer.getWireFormatVersion()#

读取底层有线格式版本。可能主要用于读取旧的有线格式版本的旧版代码不能在 .readHeader() 之前调用。

Reads the underlying wire format version. Likely mostly to be useful to legacy code reading old wire format versions. May not be called before .readHeader().

deserializer.readUint32()#

读取原始的 32 位无符号整数并返回。用于自定义的 deserializer._readHostObject() 内部。

Read a raw 32-bit unsigned integer and return it. For use inside of a custom deserializer._readHostObject().

deserializer.readUint64()#

读取原始的 64 位无符号整数并将其作为具有两个 32 位无符号整数条目的数组 [hi, lo] 返回。用于自定义的 deserializer._readHostObject() 内部。

Read a raw 64-bit unsigned integer and return it as an array [hi, lo] with two 32-bit unsigned integer entries. For use inside of a custom deserializer._readHostObject().

deserializer.readDouble()#

读取 JS number 值。用于自定义的 deserializer._readHostObject() 内部。

Read a JS number value. For use inside of a custom deserializer._readHostObject().

deserializer.readRawBytes(length)#

从反序列化器的内部缓冲区读取原始字节。length 参数必须对应于传给 serializer.writeRawBytes() 的缓冲区的长度。用于自定义的 deserializer._readHostObject() 内部。

Read raw bytes from the deserializer's internal buffer. The length parameter must correspond to the length of the buffer that was passed to serializer.writeRawBytes(). For use inside of a custom deserializer._readHostObject().

deserializer._readHostObject()#

调用此方法来读取某种宿主对象,即由原生 C++ 绑定创建的对象。如果无法反序列化数据,则应抛出合适的异常。

This method is called to read some kind of host object, i.e. an object that is created by native C++ bindings. If it is not possible to deserialize the data, a suitable exception should be thrown.

此方法不存在于 Deserializer 类本身,但可以由子类提供。

This method is not present on the Deserializer class itself but can be provided by subclasses.

类:v8.DefaultSerializer#

Class: v8.DefaultSerializer

Serializer 的子类,将 TypedArray(特别是 Buffer)和 DataView 对象序列化为宿主对象,并且只存储它们所引用的底层 ArrayBuffer 的一部分。

A subclass of Serializer that serializes TypedArray (in particular Buffer) and DataView objects as host objects, and only stores the part of their underlying ArrayBuffers that they are referring to.

类:v8.DefaultDeserializer#

Class: v8.DefaultDeserializer

DefaultSerializer 所写格式对应的 Deserializer 子类。

A subclass of Deserializer corresponding to the format written by DefaultSerializer.

Promise 钩子#

Promise hooks

promiseHooks 接口可用于跟踪 promise 生命周期事件。要跟踪所有异步活动,请参阅 async_hooks,它在内部使用此模块来生成 promise 生命周期事件以及其他异步资源的事件。请求的上下文管理参阅 AsyncLocalStorage

The promiseHooks interface can be used to track promise lifecycle events. To track all async activity, see async_hooks which internally uses this module to produce promise lifecycle events in addition to events for other async resources. For request context management, see 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 钩子必须是普通函数。提供异步函数会抛出异常,因为它会产生无限的微任务循环。

The init hook must be a plain function. Providing an async function will throw as it would produce an infinite microtask loop.

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 钩子必须是普通函数。提供异步函数会抛出异常,因为它会产生无限的微任务循环。

The settled hook must be a plain function. Providing an async function will throw as it would produce an infinite microtask loop.

import { promiseHooks } from 'node:v8';

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

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

promiseHooks.onBefore(before)#

before 钩子必须是普通函数。提供异步函数会抛出异常,因为它会产生无限的微任务循环。

The before hook must be a plain function. Providing an async function will throw as it would produce an infinite microtask loop.

import { promiseHooks } from 'node:v8';

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

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

promiseHooks.onAfter(after)#

after 钩子必须是普通函数。提供异步函数会抛出异常,因为它会产生无限的微任务循环。

The after hook must be a plain function. Providing an async function will throw as it would produce an infinite microtask loop.

import { promiseHooks } from 'node:v8';

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

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

promiseHooks.createHook(callbacks)#

钩子回调必须是普通函数。提供异步函数会抛出异常,因为它会产生无限的微任务循环。

The hook callbacks must be plain functions. Providing async functions will throw as it would produce an infinite microtask loop.

注册要为每个 promise 的不同生命周期事件调用的函数。

Registers functions to be called for different lifetime events of each promise.

回调 init()/before()/after()/settled() 在 promise 的生命周期内为各个事件调用。

The callbacks init()/before()/after()/settled() are called for the respective events during a promise's lifetime.

所有回调都是可选的。例如,如果只需要跟踪 promise 的创建,则只需要传入 init 回调。可以传递给 callbacks 的所有函数的细节在 钩子回调 部分。

All callbacks are optional. For example, if only promise creation needs to be tracked, then only the init callback needs to be passed. The specifics of all functions that can be passed to callbacks is in the Hook Callbacks section.

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

promise 生命周期中的关键事件分为四个方面:创建 promise,在调用延续处理程序之前/之后或在等待期间,以及 promise 解决或拒绝时。

Key events in the lifetime of a promise have been categorized into four areas: creation of a promise, before/after a continuation handler is called or around an await, and when the promise resolves or rejects.

虽然这些钩子与 async_hooks 的钩子相似,但它们缺少 destroy 钩子。其他类型的异步资源通常代表套接字或文件描述符,它们具有不同的 "closed" 状态来表达 destroy 生命周期事件,而只要代码仍然可以访问它们,promise 就一直可用。垃圾收集跟踪用于使 promise 适合 async_hooks 事件模型,但是这种跟踪非常昂贵,而且它们甚至不一定会被垃圾收集。

While these hooks are similar to those of async_hooks they lack a destroy hook. Other types of async resources typically represent sockets or file descriptors which have a distinct "closed" state to express the destroy lifecycle event while promises remain usable for as long as code can still reach them. Garbage collection tracking is used to make promises fit into the async_hooks event model, however this tracking is very expensive and they may not necessarily ever even be garbage collected.

因为 promise 是异步资源,其生命周期通过 promise 钩子机制进行跟踪,所以 init()before()after()settled() 回调不能是异步函数,因为它们会创建更多会产生无限循环的 promise。

Because promises are asynchronous resources whose lifecycle is tracked via the promise hooks mechanism, the init(), before(), after(), and settled() callbacks must not be async functions as they create more promises which would produce an infinite loop.

虽然此 API 用于将 promise 事件提供给 async_hooks,但两者之间的顺序是未定义的。两个 API 都是多租户的,因此可以以相对于彼此的任何顺序产生事件。

While this API is used to feed promise events into async_hooks, the ordering between the two is undefined. Both APIs are multi-tenant and therefore could produce events in any order relative to each other.

init(promise, parent)#
  • promise <Promise> 正在创建的 promise。

    promise <Promise> The promise being created.

  • parent <Promise> 如果适用,则 promise 从此继续。

    parent <Promise> The promise continued from, if applicable.

当构造 promise 时调用。这并不意味着会发生相应的 before/after 事件,只是存在这种可能性。如果在没有获得继续的情况下创建了 promise,则会发生这种情况。

Called when a promise is constructed. This does not mean that corresponding before/after events will occur, only that the possibility exists. This will happen if a promise is created without ever getting a continuation.

before(promise)#

在 promise 继续执行之前调用。这可以是 then()catch()finally() 句柄或 await 恢复的形式。

Called before a promise continuation executes. This can be in the form of then(), catch(), or finally() handlers or an await resuming.

before 回调将被调用 0 到 N 次。如果 promise 没有继续进行,则 before 回调通常会被调用 0 次。before 回调可能会在同一个 promise 进行了许多继续的情况下被多次调用。

The before callback will be called 0 to N times. The before callback will typically be called 0 times if no continuation was ever made for the promise. The before callback may be called many times in the case where many continuations have been made from the same promise.

after(promise)#

在 promise 继续执行后立即调用。这可能在 then()catch()finally() 句柄之后,或者在另一个 await 之后的 await 之前。

Called immediately after a promise continuation executes. This may be after a then(), catch(), or finally() handler or before an await after another await.

settled(promise)#

当 promise 收到解决或拒绝值时调用。这可能在 Promise.resolve()Promise.reject() 的情况下同步地发生。

Called when the promise receives a resolution or rejection value. This may occur synchronously in the case of Promise.resolve() or Promise.reject().

启动快照 API#

Startup Snapshot API

稳定性: 1 - 实验性的

Stability: 1 - Experimental

v8.startupSnapshot 接口可用于为自定义启动快照添加序列化和反序列化钩子。

The v8.startupSnapshot interface can be used to add serialization and deserialization hooks for custom startup snapshots.

$ 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 包含以下脚本:

In the example above, entry.js can use methods from the v8.startupSnapshot interface to specify how to save information for custom objects in the snapshot during serialization and how the information can be used to synchronize these objects during deserialization of the snapshot. For example, if the entry.js contains the following script:

'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

The resulted binary will get print the data deserialized from the snapshot during start up, using the refreshed process.env and process.argv of the launched process:

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

目前从用户态快照反序列化的应用无法再次快照,因此这些 API 仅适用于未从用户态快照反序列化的应用。

Currently the application deserialized from a user-land snapshot cannot be snapshotted again, so these APIs are only available to applications that are not deserialized from a user-land snapshot.

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

  • callback <Function> 在序列化之前调用的回调。

    callback <Function> Callback to be invoked before serialization.

  • data <any> 调用时将传给 callback 的可选数据。

    data <any> Optional data that will be passed to the callback when it gets called.

添加一个回调,当 Node.js 实例即将序列化为快照并退出时将调用该回调。这可以用来释放不应该或者不能序列化的资源或者将用户数据转换成更适合序列化的形式。

Add a callback that will be called when the Node.js instance is about to get serialized into a snapshot and exit. This can be used to release resources that should not or cannot be serialized or to convert user data into a form more suitable for serialization.

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

  • callback <Function> 快照反序列化后调用的回调。

    callback <Function> Callback to be invoked after the snapshot is deserialized.

  • data <any> 调用时将传给 callback 的可选数据。

    data <any> Optional data that will be passed to the callback when it gets called.

添加从快照反序列化 Node.js 实例时将调用的回调。callbackdata(如果提供)将被序列化到快照中,它们可用于重新初始化应用的状态,或者在应用从快照重新启动时重新获取应用所需的资源。

Add a callback that will be called when the Node.js instance is deserialized from a snapshot. The callback and the data (if provided) will be serialized into the snapshot, they can be used to re-initialize the state of the application or to re-acquire resources that the application needs when the application is restarted from the snapshot.

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

  • callback <Function> 快照反序列化后作为入口点调用的回调。

    callback <Function> Callback to be invoked as the entry point after the snapshot is deserialized.

  • data <any> 调用时将传给 callback 的可选数据。

    data <any> Optional data that will be passed to the callback when it gets called.

这设置了从快照反序列化 Node.js 应用的入口点。在快照构建脚本中只能调用一次。如果被调用,反序列化应用不再需要额外的入口点脚本来启动,并且只需调用回调以及反序列化数据(如果提供),否则仍需要向反序列化应用提供入口点脚本。

This sets the entry point of the Node.js application when it is deserialized from a snapshot. This can be called only once in the snapshot building script. If called, the deserialized application no longer needs an additional entry point script to start up and will simply invoke the callback along with the deserialized data (if provided), otherwise an entry point script still needs to be provided to the deserialized application.

v8.startupSnapshot.isBuildingSnapshot()#

如果运行 Node.js 实例来构建快照,则返回 true。

Returns true if the Node.js instance is run to build a snapshot.

类:v8.GCProfiler#

Class: v8.GCProfiler

此 API 在当前线程中收集 GC 数据。

This API collects GC data in current thread.

new v8.GCProfiler()#

创建 v8.GCProfiler 类的新实例。

Create a new instance of the v8.GCProfiler class.

profiler.start()#

开始收集 GC 数据。

Start collecting GC data.

profiler.stop()#

停止收集 GC 数据,返回一个对象,对象内容如下。

Stop collecting GC data and return an object.The content of object is as follows.

{
  "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
} 

这是一个例子。

Here's an example.

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