Node.js v18.4.0 文档


目录

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
// v8.cachedDataVersionTag() 返回的值源自
// V8 版本、命令行标志、以及检测到的 CPU 特性。
// 测试该值是否确实在切换标志时更新。
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()#

中英对照

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

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

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

// 打印堆快照到控制台
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 命令行标志。 此方法需谨慎使用。 在虚拟机启动后更改设置可能会导致不可预测的行为,包括崩溃和数据丢失;或者它可能只是什么都不做。

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

用法:

// 将 GC 事件打印到标准输出一分钟。
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])#

中英对照

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

生成当前 V8 堆的快照并将其写入 JSON 文件。 此文件旨在与 Chrome 开发者工具等工具一起使用 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}`);
    // 现在获取主线程的堆转储。
    console.log(`main thread heapdump: ${writeHeapSnapshot()}`);
  });

  // 告诉工作进程创建堆转储。
  worker.postMessage('heapdump');
} else {
  parentPort.once('message', (message) => {
    if (message === 'heapdump') {
      // 为工作进程生成堆转储,
      // 并将文件名返回给父进程。
      parentPort.postMessage(writeHeapSnapshot());
    }
  });
}

序列化 API#

中英对照

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

格式是向后兼容的(即可以安全地存储到磁盘)。 相同的 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 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#

中英对照

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

Promise 钩子#

中英对照

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

import { promiseHooks } from 'node:v8';

// promise 产生了四个生命周期事件:

// `init` 事件代表了 promise 的创建。
// 这可以是直接创建,例如使用 `new Promise(...)`,
// 或者是继续,例如 `then()` 或 `catch()`。
// 每当调用异步函数或执行“等待”时,也会发生这种情况。
// 如果创建了继续 promise,则 `parent` 将是它作为继续的 promise。
function init(promise, parent) {
  console.log('a promise was created', { promise, parent });
}

// `settled` 事件在 promise 收到解决或拒绝值时发生。
// 这可能会同步地发生,
// 例如在非 promise 输入上使用 `Promise.resolve()` 时。
function settled(promise) {
  console.log('a promise resolved or rejected', { promise });
}

// `before` 事件在 `then()` 或 `catch()` 句柄运行
// 或 `await` 恢复执行之前立即运行。
function before(promise) {
  console.log('a promise is about to call a then handler', { promise });
}

// `after` 事件在 `then()` 句柄运行之后
// 或 `await` 从另一个句柄恢复之后立即运行。
function after(promise) {
  console.log('a promise is done calling a then handler', { promise });
}

// 生命周期钩子可以单独启动和停止
const stopWatchingInits = promiseHooks.onInit(init);
const stopWatchingSettleds = promiseHooks.onSettled(settled);
const stopWatchingBefores = promiseHooks.onBefore(before);
const stopWatchingAfters = promiseHooks.onAfter(after);

// 或者它们可以分组启动和停止
const stopHookSet = promiseHooks.createHook({
  init,
  settled,
  before,
  after
});

// 要停止钩子,则调用创建时返回的函数。
stopWatchingInits();
stopWatchingSettleds();
stopWatchingBefores();
stopWatchingAfters();
stopHookSet();

promiseHooks.onInit(init)#

中英对照

  • init <Function> 当创建 promise 时调用的 init 回调
  • 返回: <Function> 调用以停止钩子。 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 <Function> 当 promise 被解决或拒绝时调用的 settled 回调
  • 返回: <Function> 调用以停止钩子。 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 <Function> 在 promise 继续执行之前调用的 before 回调
  • 返回: <Function> 调用以停止钩子。 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 <Function> promise 继续执行后要调用的 after 回调
  • 返回: <Function> 调用以停止钩子。 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 次。 before 回调可能会在同一个 promise 进行了许多继续的情况下被多次调用。

after(promise)#

中英对照

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

settled(promise)#

中英对照

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

返回顶部