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