Skip to content

V8

Исходный код: lib/v8.js

Модуль node:v8 предоставляет API, специфичные для версии V8, встроенной в бинарный файл Node.js. Доступ к нему можно получить с помощью:

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

v8.cachedDataVersionTag()

Добавлено в: v8.0.0

Возвращает целое число, представляющее тег версии, полученный из версии V8, флагов командной строки и обнаруженных функций ЦП. Это полезно для определения того, совместим ли буфер cachedData vm.Script с этим экземпляром V8.

js
console.log(v8.cachedDataVersionTag()); // 3947234607
// Значение, возвращаемое v8.cachedDataVersionTag(), выводится из версии V8,
// флагов командной строки и обнаруженных функций ЦП. Проверьте, действительно ли значение
// обновляется при переключении флагов.
v8.setFlagsFromString('--allow_natives_syntax');
console.log(v8.cachedDataVersionTag()); // 183726201

v8.getHeapCodeStatistics()

Добавлено в: v12.8.0

Получает статистику о коде и его метаданных в куче, см. V8 API GetHeapCodeAndMetadataStatistics. Возвращает объект со следующими свойствами:

js
{
  code_and_metadata_size: 212208,
  bytecode_and_metadata_size: 161368,
  external_script_source_size: 1410794,
  cpu_profiler_metadata_size: 0,
}

v8.getHeapSnapshot([options])

[История изменений]

ВерсияИзменения
v19.1.0Поддержка опций для настройки снапшота кучи.
v11.13.0Добавлено в: v11.13.0
  • options <Object>

    • exposeInternals <boolean> Если true, предоставляет внутренние компоненты в снапшоте кучи. По умолчанию: false.
    • exposeNumericValues <boolean> Если true, предоставляет числовые значения в искусственных полях. По умолчанию: false.
  • Возвращает: <stream.Readable> Readable-поток, содержащий снапшот кучи V8.

Создаёт снапшот текущей кучи V8 и возвращает Readable-поток, который можно использовать для чтения JSON-сериализованного представления. Этот формат JSON-потока предназначен для использования с такими инструментами, как Chrome DevTools. JSON-схема не документирована и специфична для движка V8. Следовательно, схема может изменяться от одной версии V8 к другой.

Создание снапшота кучи требует памяти примерно в два раза больше, чем размер кучи на момент создания снапшота. Это приводит к риску завершения процесса OOM killer'ом.

Создание снапшота - это синхронная операция, которая блокирует цикл событий на время, зависящее от размера кучи.

js
// Вывод снапшота кучи в консоль
const v8 = require('node:v8');
const stream = v8.getHeapSnapshot();
stream.pipe(process.stdout);

v8.getHeapSpaceStatistics()

[История изменений]

ВерсияИзменения
v7.5.0Поддержка значений, превышающих диапазон 32-битного целого числа без знака.
v6.0.0Добавлено в: v6.0.0

Возвращает статистику о пространствах кучи V8, то есть о сегментах, составляющих кучу V8. Ни порядок пространств кучи, ни доступность пространства кучи не могут быть гарантированы, поскольку статистика предоставляется через функцию V8 GetHeapSpaceStatistics и может изменяться от одной версии V8 к другой.

Возвращаемое значение - это массив объектов, содержащих следующие свойства:

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

[История]

ВерсияИзменения
v7.5.0Поддержка значений, превышающих диапазон 32-битного целого числа без знака.
v7.2.0Добавлены malloced_memory, peak_malloced_memory и does_zap_garbage.
v1.0.0Добавлено в: v1.0.0

Возвращает объект со следующими свойствами:

total_heap_size Значение total_heap_size - это количество байтов, выделенных V8 для кучи. Это значение может увеличиваться, если used_heap требуется больше памяти.

total_heap_size_executable Значение total_heap_size_executable - это часть кучи, которая может содержать исполняемый код, в байтах. Это включает память, используемую JIT-скомпилированным кодом, и любую память, которая должна оставаться исполняемой.

total_physical_size Значение total_physical_size - это фактическая физическая память, используемая кучей V8, в байтах. Это объем зафиксированной (или используемой) памяти, а не зарезервированной.

total_available_size Значение total_available_size - это количество байтов памяти, доступной для кучи V8. Это значение показывает, сколько еще памяти может использовать V8, прежде чем превысит предел кучи.

used_heap_size Значение used_heap_size - это количество байтов, используемых в настоящее время JavaScript-объектами V8. Это фактическая используемая память и не включает память, которая была выделена, но еще не использована.

heap_size_limit Значение heap_size_limit - это максимальный размер кучи V8 в байтах (либо предел по умолчанию, определяемый системными ресурсами, либо значение, переданное параметру --max_old_space_size).

malloced_memory Значение malloced_memory - это количество байтов, выделенных V8 через malloc.

peak_malloced_memory Значение peak_malloced_memory - это пиковое количество байтов, выделенных V8 через malloc за время существования процесса.

does_zap_garbage - это логическое значение 0/1, которое указывает, включен или нет параметр --zap_code_space. Это заставляет V8 перезаписывать мусор в куче битовым шаблоном. След RSS (resident set size) становится больше, потому что он постоянно касается всех страниц кучи, и это снижает вероятность их выгрузки операционной системой.

number_of_native_contexts Значение number_of_native_contexts - это количество активных в настоящее время контекстов верхнего уровня. Увеличение этого числа с течением времени указывает на утечку памяти.

number_of_detached_contexts Значение number_of_detached_contexts - это количество контекстов, которые были отсоединены и еще не собраны сборщиком мусора. Ненулевое значение этого числа указывает на потенциальную утечку памяти.

total_global_handles_size Значение total_global_handles_size - это общий размер памяти глобальных дескрипторов V8.

used_global_handles_size Значение used_global_handles_size - это используемый размер памяти глобальных дескрипторов V8.

external_memory Значение external_memory - это размер памяти буферов массивов и внешних строк.

js
{
  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.queryObjects(ctor[, options])

Добавлено в: v22.0.0, v20.13.0

[Stable: 1 - Experimental]

Stable: 1 Stability: 1.1 - Активная разработка

  • ctor <Function> Конструктор, который может быть использован для поиска в цепочке прототипов с целью фильтрации целевых объектов в куче.

  • options <undefined> | <Object>

    • format <string> Если это 'count', возвращается количество найденных объектов. Если это 'summary', возвращается массив со строками, содержащими краткое описание найденных объектов.
  • Возвращает: {number|Array

Это аналогично queryObjects() API консоли, предоставляемому консолью Chromium DevTools. Его можно использовать для поиска объектов, имеющих соответствующий конструктор в своей цепочке прототипов в куче после полной сборки мусора, что может быть полезно для регрессионных тестов на утечки памяти. Чтобы избежать неожиданных результатов, пользователям следует избегать использования этого API для конструкторов, реализацию которых они не контролируют, или для конструкторов, которые могут быть вызваны другими сторонами в приложении.

Чтобы избежать случайных утечек, этот API не возвращает необработанные ссылки на найденные объекты. По умолчанию он возвращает количество найденных объектов. Если options.format равно 'summary', он возвращает массив, содержащий краткие строковые представления для каждого объекта. Видимость, предоставляемая в этом API, аналогична той, которую предоставляет снимок кучи, в то время как пользователи могут сэкономить на сериализации и анализе и напрямую фильтровать целевые объекты во время поиска.

В результаты включаются только объекты, созданные в текущем контексте выполнения.

js
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' }));
js
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)

Добавлено в версии: v1.0.0

Метод v8.setFlagsFromString() можно использовать для программной установки флагов командной строки V8. Этот метод следует использовать с осторожностью. Изменение настроек после запуска VM может привести к непредсказуемому поведению, включая сбои и потерю данных; или это может просто ничего не дать.

Параметры V8, доступные для версии Node.js, можно определить, запустив node --v8-options.

Использование:

js
// Печатать события GC в stdout в течение одной минуты.
const v8 = require('node:v8');
v8.setFlagsFromString('--trace_gc');
setTimeout(() => { v8.setFlagsFromString('--notrace_gc'); }, 60e3);

v8.stopCoverage()

Добавлено в версии: v15.1.0, v14.18.0, v12.22.0

Метод v8.stopCoverage() позволяет пользователю остановить сбор покрытия, запущенный с помощью NODE_V8_COVERAGE, чтобы V8 могла освободить записи количества выполненных операций и оптимизировать код. Это можно использовать в сочетании с v8.takeCoverage(), если пользователь хочет собрать покрытие по требованию.

v8.takeCoverage()

Добавлено в версии: v15.1.0, v14.18.0, v12.22.0

Метод v8.takeCoverage() позволяет пользователю записывать покрытие, запущенное с помощью NODE_V8_COVERAGE, на диск по требованию. Этот метод можно вызывать несколько раз в течение жизненного цикла процесса. Каждый раз счетчик выполнения будет сбрасываться, и новый отчет о покрытии будет записываться в каталог, указанный в NODE_V8_COVERAGE.

Когда процесс собирается завершиться, последнее покрытие все равно будет записано на диск, если v8.stopCoverage() не будет вызван до завершения процесса.

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

[История]

ВерсияИзменения
v19.1.0Поддержка параметров для настройки снимка кучи.
v18.0.0Теперь будет выброшено исключение, если файл не может быть записан.
v18.0.0Сделать возвращаемые коды ошибок согласованными на всех платформах.
v11.13.0Добавлено в версии: v11.13.0
  • filename <string> Путь к файлу, в котором должен быть сохранен снимок кучи V8. Если не указан, будет сгенерировано имя файла с шаблоном 'Heap-${yyyymmdd}-${hhmmss}-${pid}-${thread_id}.heapsnapshot', где {pid} будет PID процесса Node.js, {thread_id} будет 0, когда writeHeapSnapshot() вызывается из основного потока Node.js, или идентификатор рабочего потока.

  • options <Object>

    • exposeInternals <boolean> Если true, раскрывать внутренние данные в снимке кучи. По умолчанию: false.
    • exposeNumericValues <boolean> Если true, раскрывать числовые значения в искусственных полях. По умолчанию: false.
  • Возвращает: <string> Имя файла, в котором был сохранен снимок.

Создает снимок текущей кучи V8 и записывает его в файл JSON. Этот файл предназначен для использования с такими инструментами, как Chrome DevTools. Схема JSON не документирована и специфична для движка V8 и может изменяться от одной версии V8 к другой.

Снимок кучи специфичен для одного изолированного экземпляра V8. При использовании рабочих потоков снимок кучи, сгенерированный из основного потока, не будет содержать никакой информации о рабочих потоках, и наоборот.

Создание снимка кучи требует памяти примерно вдвое больше размера кучи на момент создания снимка. Это приводит к риску завершения процесса с помощью OOM killer.

Создание снимка - это синхронная операция, которая блокирует цикл событий на время, зависящее от размера кучи.

js
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());
    }
  });
}

v8.setHeapSnapshotNearHeapLimit(limit)

Добавлено в: v18.10.0, v16.18.0

[Stable: 1 - Experimental]

Stable: 1 Stability: 1 - Экспериментально

API не выполняет никаких действий, если --heapsnapshot-near-heap-limit уже установлен из командной строки или API вызывается более одного раза. limit должен быть положительным целым числом. Смотрите --heapsnapshot-near-heap-limit для получения дополнительной информации.

API сериализации

API сериализации предоставляет средства для сериализации значений JavaScript способом, совместимым с алгоритмом структурированного клонирования HTML.

Формат обратно совместим (то есть его безопасно хранить на диске). Равные значения JavaScript могут приводить к разным сериализованным выходным данным.

v8.serialize(value)

Добавлено в: v8.0.0

Использует DefaultSerializer для сериализации value в буфер.

ERR_BUFFER_TOO_LARGE будет выброшено при попытке сериализовать огромный объект, требующий буфер больше, чем buffer.constants.MAX_LENGTH.

v8.deserialize(buffer)

Добавлено в: v8.0.0

Использует DefaultDeserializer с параметрами по умолчанию для чтения JS значения из буфера.

Класс: v8.Serializer

Добавлено в: v8.0.0

new Serializer()

Создает новый объект Serializer.

serializer.writeHeader()

Записывает заголовок, включающий версию формата сериализации.

serializer.writeValue(value)

Сериализует значение JavaScript и добавляет сериализованное представление во внутренний буфер.

Выбрасывает ошибку, если value не может быть сериализовано.

serializer.releaseBuffer()

Возвращает сохраненный внутренний буфер. Этот сериализатор не должен использоваться после освобождения буфера. Вызов этого метода приводит к неопределенному поведению, если предыдущая запись завершилась неудачей.

serializer.transferArrayBuffer(id, arrayBuffer)

  • id <integer> 32-битное целое число без знака.
  • arrayBuffer <ArrayBuffer> Экземпляр 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 без знака для объекта, используя тот же ID, если этот SharedArrayBuffer уже был сериализован. При десериализации этот ID будет передан в deserializer.transferArrayBuffer().

Если объект не может быть сериализован, должно быть сгенерировано исключение.

Этот метод отсутствует в самом классе Serializer, но может быть предоставлен подклассами.

serializer._setTreatArrayBufferViewsAsHostObjects(flag)

  • flag <boolean> По умолчанию: false

Указывает, следует ли рассматривать объекты TypedArray и DataView как хост-объекты, т.е. передавать их в serializer._writeHostObject().

Класс: v8.Deserializer

Добавлено в: v8.0.0

new Deserializer(buffer)

Создает новый объект Deserializer.

deserializer.readHeader()

Считывает и проверяет заголовок (включая версию формата). Может, например, отклонить недействительный или неподдерживаемый проводной формат. В этом случае выбрасывается Error.

deserializer.readValue()

Десериализует значение JavaScript из буфера и возвращает его.

deserializer.transferArrayBuffer(id, arrayBuffer)

Помечает ArrayBuffer как имеющий содержимое, переданное вне диапазона. Передайте соответствующий ArrayBuffer в контексте сериализации в serializer.transferArrayBuffer() (или верните id из serializer._getSharedArrayBufferId() в случае SharedArrayBuffers).

deserializer.getWireFormatVersion()

Считывает базовую версию формата передачи данных. Вероятно, в основном полезно для устаревшего кода, читающего старые версии формата передачи данных. Не может быть вызван до .readHeader().

deserializer.readUint32()

Считывает необработанное 32-битное целое число без знака и возвращает его. Для использования внутри пользовательского deserializer._readHostObject().

deserializer.readUint64()

Считывает необработанное 64-битное целое число без знака и возвращает его в виде массива [hi, lo] с двумя 32-битными целыми числами без знака. Для использования внутри пользовательского deserializer._readHostObject().

deserializer.readDouble()

Считывает значение JS number. Для использования внутри пользовательского deserializer._readHostObject().

deserializer.readRawBytes(length)

Считывает необработанные байты из внутреннего буфера десериализатора. Параметр length должен соответствовать длине буфера, который был передан в serializer.writeRawBytes(). Для использования внутри пользовательского deserializer._readHostObject().

deserializer._readHostObject()

Этот метод вызывается для чтения какого-либо хост-объекта, т.е. объекта, созданного собственными привязками C++. Если десериализовать данные невозможно, следует выбросить соответствующее исключение.

Этот метод отсутствует в самом классе Deserializer, но может быть предоставлен подклассами.

Класс: v8.DefaultSerializer

Добавлено в: v8.0.0

Подкласс Serializer, который сериализует объекты TypedArray (в частности Buffer) и DataView как хост-объекты и хранит только ту часть их базовых ArrayBufferов, на которую они ссылаются.

Класс: v8.DefaultDeserializer

Добавлено в: v8.0.0

Подкласс Deserializer, соответствующий формату, записанному DefaultSerializer.

Хуки Promise

Интерфейс promiseHooks можно использовать для отслеживания событий жизненного цикла promise. Чтобы отслеживать всю асинхронную активность, см. async_hooks, который внутренне использует этот модуль для создания событий жизненного цикла promise в дополнение к событиям для других асинхронных ресурсов. Для управления контекстом запросов см. AsyncLocalStorage.

js
import { promiseHooks } from 'node:v8';

// Существует четыре события жизненного цикла, создаваемые promise:

// Событие `init` представляет собой создание promise. Это может быть
// прямое создание, например, с помощью `new Promise(...)`, или продолжение,
// например, `then()` или `catch()`. Это также происходит всякий раз, когда вызывается
// асинхронная функция или выполняет `await`. Если создается promise продолжения,
// `parent` будет promise, из которого он является продолжением.
function init(promise, parent) {
  console.log('promise был создан', { promise, parent });
}

// Событие `settled` происходит, когда promise получает значение разрешения или
// отклонения. Это может произойти синхронно, например, при использовании
// `Promise.resolve()` для ввода, не являющегося promise.
function settled(promise) {
  console.log('promise разрешен или отклонен', { promise });
}

// Событие `before` запускается непосредственно перед запуском обработчика `then()` или `catch()`
// или возобновлением выполнения `await`.
function before(promise) {
  console.log('promise собирается вызвать обработчик then', { promise });
}

// Событие `after` запускается непосредственно после запуска обработчика `then()` или когда
// начинается `await` после возобновления выполнения из другого.
function after(promise) {
  console.log('promise завершил вызов обработчика then', { 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)

Добавлено в: v17.1.0, v16.14.0

  • init <Function> init callback, который вызывается при создании промиса.
  • Возвращает: <Function> Вызов для остановки хука.

Хук init должен быть простой функцией. Предоставление асинхронной функции вызовет ошибку, так как это приведет к бесконечному циклу микрозадач.

js
import { promiseHooks } from 'node:v8';

const stop = promiseHooks.onInit((promise, parent) => {});
js
const { promiseHooks } = require('node:v8');

const stop = promiseHooks.onInit((promise, parent) => {});

promiseHooks.onSettled(settled)

Добавлено в: v17.1.0, v16.14.0

  • settled <Function> settled callback, который вызывается, когда промис выполнен или отклонен.
  • Возвращает: <Function> Вызов для остановки хука.

Хук settled должен быть простой функцией. Предоставление асинхронной функции вызовет ошибку, так как это приведет к бесконечному циклу микрозадач.

js
import { promiseHooks } from 'node:v8';

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

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

promiseHooks.onBefore(before)

Добавлено в: v17.1.0, v16.14.0

  • before <Function> before callback, который вызывается перед выполнением продолжения промиса.
  • Возвращает: <Function> Вызов для остановки хука.

Хук before должен быть простой функцией. Предоставление асинхронной функции вызовет ошибку, так как это приведет к бесконечному циклу микрозадач.

js
import { promiseHooks } from 'node:v8';

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

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

promiseHooks.onAfter(after)

Добавлено в: v17.1.0, v16.14.0

  • after <Function> after callback, вызываемый после выполнения продолжения промиса.
  • Возвращает: <Function> Вызов для остановки хука.

Хук after должен быть обычной функцией. Использование асинхронной функции приведет к ошибке, поскольку это приведет к бесконечному циклу микрозадач.

js
import { promiseHooks } from 'node:v8';

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

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

promiseHooks.createHook(callbacks)

Добавлено в: v17.1.0, v16.14.0

Callback-функции для хуков должны быть обычными функциями. Использование асинхронных функций приведет к ошибке, поскольку это приведет к бесконечному циклу микрозадач.

Регистрирует функции, которые будут вызываться для различных событий жизненного цикла каждого промиса.

Callback-функции init()/before()/after()/settled() вызываются для соответствующих событий в течение жизненного цикла промиса.

Все callback-функции необязательны. Например, если нужно отслеживать только создание промиса, то нужно передать только callback-функцию init. Подробности обо всех функциях, которые можно передать в callbacks, находятся в разделе Callback-функции для хуков.

js
import { promiseHooks } from 'node:v8';

const stopAll = promiseHooks.createHook({
  init(promise, parent) {},
});
js
const { promiseHooks } = require('node:v8');

const stopAll = promiseHooks.createHook({
  init(promise, parent) {},
});

Хуки обратного вызова

Ключевые события в жизненном цикле промиса были категоризированы по четырём областям: создание промиса, до/после вызова обработчика продолжения или вокруг await, и когда промис разрешается или отклоняется.

Хотя эти хуки похожи на хуки async_hooks, у них отсутствует хук destroy. Другие типы асинхронных ресурсов обычно представляют собой сокеты или файловые дескрипторы, которые имеют отчетливое "закрытое" состояние для выражения события жизненного цикла destroy, в то время как промисы остаются пригодными для использования до тех пор, пока код может до них добраться. Отслеживание сборки мусора используется для того, чтобы подогнать промисы под модель событий async_hooks, однако это отслеживание очень дорогостоящее, и они могут даже никогда не быть собраны сборщиком мусора.

Поскольку промисы являются асинхронными ресурсами, жизненный цикл которых отслеживается с помощью механизма хуков промисов, обратные вызовы init(), before(), after() и settled() не должны быть асинхронными функциями, поскольку они создают больше промисов, что приведет к бесконечному циклу.

Хотя этот API используется для передачи событий промисов в async_hooks, порядок между ними не определен. Оба API являются мультиарендными и поэтому могут создавать события в любом порядке относительно друг друга.

init(promise, parent)

  • promise <Promise> Создаваемый промис.
  • parent <Promise> Промис, продолженный от, если применимо.

Вызывается при создании промиса. Это не означает, что произойдут соответствующие события before/after, только то, что такая возможность существует. Это произойдет, если промис создан без получения продолжения.

before(promise)

Вызывается перед выполнением продолжения промиса. Это может быть в форме обработчиков then(), catch() или finally() или возобновления await.

Обратный вызов before будет вызываться от 0 до N раз. Обратный вызов before обычно вызывается 0 раз, если для промиса никогда не было создано продолжение. Обратный вызов before может быть вызван много раз в случае, когда из одного и того же промиса было сделано много продолжений.

after(promise)

Вызывается сразу после выполнения продолжения промиса. Это может произойти после обработчика then(), catch() или finally() или перед await после другого await.

settled(promise)

Вызывается, когда промис получает значение разрешения или отклонения. Это может произойти синхронно в случае Promise.resolve() или Promise.reject().

API Startup Snapshot

Добавлено в: v18.6.0, v16.17.0

[Stable: 1 - Experimental]

Stable: 1 Стабильность: 1 - Экспериментальный

Интерфейс v8.startupSnapshot можно использовать для добавления хуков сериализации и десериализации для пользовательских снапшотов запуска.

bash
$ 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 содержит следующий скрипт:

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

  // Чтение серии файлов из каталога и сохранение их в хранилище.
  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 здесь - это место, где скрипт снапшота размещается
// во время построения снапшота.
const shelf = new BookShelf(__dirname, [
  'book1.en_US.txt',
  'book1.es_ES.txt',
  'book2.zh_CN.txt',
]);

assert(v8.startupSnapshot.isBuildingSnapshot());
// При сериализации снапшота сжимаем книги для уменьшения размера.
v8.startupSnapshot.addSerializeCallback(BookShelf.compressAll, shelf);
// При десериализации снапшота распаковываем книги.
v8.startupSnapshot.addDeserializeCallback(BookShelf.decompressAll, shelf);
v8.startupSnapshot.setDeserializeMainFunction((shelf) => {
  // process.env и process.argv обновляются во время снапшота
  // десериализации.
  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.env и process.argv запущенного процесса:

bash
$ BOOK_LANG=es_ES node --snapshot-blob snapshot.blob book1
# Печатает содержимое book1.es_ES.txt, десериализованное из снапшота. {#prints-content-of-book1es_estxt-deserialized-from-the-snapshot}

В настоящее время приложение, десериализованное из пользовательского снапшота, не может быть повторно заснапшотено, поэтому эти API доступны только для приложений, которые не десериализованы из пользовательского снапшота.

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

Добавлено в: v18.6.0, v16.17.0

  • callback <Function> Callback, который будет вызван перед сериализацией.
  • data <any> Необязательные данные, которые будут переданы в callback при его вызове.

Добавляет callback, который будет вызван, когда экземпляр Node.js собирается быть сериализованным в снимок и завершить работу. Это можно использовать для освобождения ресурсов, которые не должны или не могут быть сериализованы, или для преобразования пользовательских данных в форму, более подходящую для сериализации.

Callback-и выполняются в том порядке, в котором они были добавлены.

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

Добавлено в: v18.6.0, v16.17.0

  • callback <Function> Callback, который будет вызван после десериализации снимка.
  • data <any> Необязательные данные, которые будут переданы в callback при его вызове.

Добавляет callback, который будет вызван, когда экземпляр Node.js десериализуется из снимка. callback и data (если предоставлены) будут сериализованы в снимок. Их можно использовать для повторной инициализации состояния приложения или для повторного получения ресурсов, которые необходимы приложению, когда приложение перезапускается из снимка.

Callback-и выполняются в том порядке, в котором они были добавлены.

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

Добавлено в: v18.6.0, v16.17.0

  • callback <Function> Callback, который будет вызван как точка входа после десериализации снимка.
  • data <any> Необязательные данные, которые будут переданы в callback при его вызове.

Устанавливает точку входа приложения Node.js при его десериализации из снимка. Это можно вызвать только один раз в скрипте создания снимка. Если он вызывается, десериализованному приложению больше не требуется дополнительный скрипт точки входа для запуска, и он просто вызовет callback вместе с десериализованными данными (если они предоставлены), в противном случае все еще необходимо предоставить скрипт точки входа десериализованному приложению.

v8.startupSnapshot.isBuildingSnapshot()

Добавлено в: v18.6.0, v16.17.0

Возвращает true, если экземпляр Node.js запущен для создания снимка.

Класс: v8.GCProfiler

Добавлено в: v19.6.0, v18.15.0

Этот API собирает данные GC в текущем потоке.

new v8.GCProfiler()

Добавлено в: v19.6.0, v18.15.0

Создает новый экземпляр класса v8.GCProfiler.

profiler.start()

Добавлено в: v19.6.0, v18.15.0

Начинает сбор данных GC.

profiler.stop()

Добавлено в: v19.6.0, v18.15.0

Прекращает сбор данных GC и возвращает объект. Содержание объекта следующее.

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

Вот пример.

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