Перейти к содержанию

V8

v18.x.x

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

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

v8.cachedDataVersionTag()

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

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

v8.getHeapCodeStatistics()

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

1
2
3
4
5
6
{
  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> Readable, содержащий снимок кучи V8.

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

Для создания моментального снимка кучи требуется память, примерно вдвое превышающая размер кучи на момент создания моментального снимка. Это приводит к риску того, что OOM killers завершит процесс.

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

1
2
3
4
// Выводим снимок кучи в консоль
const v8 = require('node:v8');
const stream = v8.getHeapSnapshot();
stream.pipe(process.stdout);

v8.getHeapSpaceStatistics()

  • Возвращает: {Объект[]}

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[
    {
        "имя_пространства": "new_space",
        "space_size": 2063872,
        "space_used_size": 951112,
        "space_available_size": 80824,
        "физический_размер_пространства": 2063872
    },
    {
        "имя_пространства": "old_space",
        "space_size": 3090560,
        "space_used_size": 2493792,
        "space_available_size": 0,
        "физический_размер_пространства": 3090560
    },
    {
        "имя_пространства": "code_space",
        "space_size": 1260160,
        "space_used_size": 644256,
        "space_available_size": 960,
        "физический_размер_пространства": 1260160
    },
    {
        "имя_пространства": "map_space",
        "space_size": 1094160,
        "space_used_size": 201608,
        "space_available_size": 0,
        "физический_размер_пространства": 1094160
    },
    {
        "имя_пространства": "large_object_space",
        "space_size": 0,
        "space_used_size": 0,
        "space_available_size": 1490980608,
        "физический_размер_пространства": 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 - это размер памяти для буферов массивов и внешних строк.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  total_heap_size: 7326976,
  total_heap_size_executable: 4194304,
  total_physical_size: 7326976,
  общий_доступный_размер: 1152656,
  используемый размер_кучи: 3476208,
  heap_size_limit: 1535115264,
  malloced_memory: 16384,
  peak_malloced_memory: 1127496,
  does_zap_garbage: 0,
  number_of_native_contexts: 1,
  число_отсоединенных_контекстов: 0,
  total_global_handles_size: 8192,
  используемый_размер_глобальных_ручек: 3296,
  внешняя_память: 318824
}

v8.setFlagsFromString(flags)

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

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

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

1
2
3
4
5
6
// Печать событий GC на stdout в течение одной минуты.
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} будет PID процесса Node.js, {thread_id} будет 0, если writeHeapSnapshot() вызывается из главного потока Node.js или id рабочего потока.
  • options <Object>
    • exposeInternals <boolean> Если true, то раскрывать внутренние данные в снимке кучи. По умолчанию: false.
    • exposeNumericValues <boolean> Если true, раскрывать числовые значения в искусственных полях. По умолчанию: false.
  • Возвращает: <string> Имя файла, в котором был сохранен снимок.

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

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

Для создания снимка кучи требуется память, примерно вдвое превышающая размер кучи на момент создания снимка. Это приводит к риску того, что OOM killers завершит процесс.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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}`);
        // Теперь получим heapdump для главного потока.
        console.log(
            `main thread heapdump: ${writeHeapSnapshot()}`
        );
    });

    // Сообщите работнику о создании дампа кучи.
    worker.postMessage('heapdump');
} else {
    parentPort.once('message', (message) => {
        if (message === 'heapdump') {
            // Генерируем heapdump для рабочего.
            // и верните имя файла родительскому порту.
            parentPort.postMessage(writeHeapSnapshot());
        }
    });
}

v8.setHeapSnapshotNearHeapLimit(limit)

Стабильность: 1 – Экспериментальная

Экспериментальная

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

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

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

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

v8.serialize(value)

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

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

v8.deserialize(buffer)

  • buffer {Buffer|TypedArray|DataView} Буфер, возвращаемый serialize().

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

Класс: v8.Serializer

new Serializer()

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

serializer.writeHeader()

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

serializer.writeValue(value)

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

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

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 числа. Для использования внутри пользовательского serializer._writeHostObject().

serializer.writeRawBytes(buffer)

  • buffer {Buffer|TypedArray|DataView}

Записывает необработанные байты во внутренний буфер сериализатора. Десериализатору потребуется способ вычисления длины буфера. Для использования внутри пользовательского 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

новый десериализатор(буфер)

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

deserializer.readHeader()

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

deserializer.readValue()

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

deserializer.transferArrayBuffer(id, arrayBuffer)

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

deserializer.getWireFormatVersion()

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

deserializer.readUint32()

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

deserializer.readUint64()

  • Возвращает: {integer[]}

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

deserializer.readDouble()

Считывает значение JS число. Для использования внутри пользовательского 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.

Крючки обещания

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import { promiseHooks } from 'node:v8';

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

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

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

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

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

// Чтобы остановить хук, вызовите функцию, возвращаемую при его создании.
stopWatchingInits();
stopWatchingSettleds();
stopWatchingBefores();
stopWatchingAfters();
stopHookSet();

promiseHooks.onInit(init)

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

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

1
2
3
import { promiseHooks } from 'node:v8';

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

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

promiseHooks.onSettled(settled)

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

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

1
2
3
import { promiseHooks } from 'node:v8';

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

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

promiseHooks.onBefore(before)

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

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

1
2
3
import { promiseHooks } from 'node:v8';

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

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

promiseHooks.onAfter(after)

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

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

1
2
3
import { promiseHooks } from 'node:v8';

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

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

promiseHooks.createHook(callbacks)

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

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

Обратные вызовы init()/ before()/ after()/ settled() вызываются для соответствующих событий во время жизни обещания.

Все обратные вызовы необязательны. Например, если необходимо отслеживать только создание обещания, то нужно передать только обратный вызов init. Специфика всех функций, которые могут быть переданы в callbacks, находится в разделе Hook Callbacks.

1
2
3
4
5
import { promiseHooks } from 'node:v8';

const stopAll = promiseHooks.createHook({
    init(promise, parent) {},
});
1
2
3
4
5
const { promiseHooks } = require('node:v8');

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

Обратные вызовы крючков

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

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

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

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

init(promise, parent)

  • promise <Promise> Создаваемое обещание.
  • parent <Promise> Обещание, от которого оно продолжается, если применимо.

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

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

Startup Snapshot API

Стабильность: 1 – Экспериментальная

Экспериментальный

Интерфейс v8.startupSnapshot можно использовать для добавления крючков сериализации и десериализации для пользовательских стартовых снимков. В настоящее время снимки запуска могут быть встроены только в бинарный файл Node.js из исходного кода.

1
2
3
4
5
$ cd /path/to/node $ ./configure --node-snapshot-main=entry.js $ make node

# Этот двоичный файл содержит результат выполнения файла entry.js

$ out/Release/node

В приведенном примере entry.js может использовать методы из интерфейса v8.startupSnapshot, чтобы указать, как сохранить информацию для пользовательских объектов в снапшоте во время сериализации и как эта информация может быть использована для синхронизации этих объектов во время десериализации снапшота. Например, если entry.js содержит следующий сценарий:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
'use strict';

const fs = require('node:fs');
const zlib = require('node:zlib');
const path = require('node:path');
const assert = require('node:assert');

const {
    isBuildingSnapshot,
    addSerializeCallback,
    addDeserializeCallback,
    setDeserializeMainFunction,
} = require('node:v8').startupSnapshot;

const filePath = path.resolve(__dirname, '../x1024.txt');
const storage = {};

assert(isBuildingSnapshot());

addSerializeCallback(
    ({ filePath }) => {
        storage[filePath] = zlib.gzipSync(
            fs.readFileSync(filePath)
        );
    },
    { filePath }
);

addDeserializeCallback(
    ({ filePath }) => {
        storage[filePath] = zlib.gunzipSync(
            storage[filePath]
        );
    },
    { filePath }
);

setDeserializeMainFunction(
    ({ filePath }) => {
        console.log(storage[filePath].toString());
    },
    { filePath }
);

Полученный двоичный файл просто выведет данные, десериализованные из моментального снимка при запуске:

1
2
3
$ out/Release/node

# Печатает содержимое ./test/fixtures/x1024.txt

В настоящее время API доступен только для экземпляра Node.js, запущенного из снапшота по умолчанию, то есть приложение, десериализованное из пользовательского снапшота, не сможет снова использовать эти 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 будет десериализован из моментального снимка. Обратный вызов и data (если предоставлены) будут сериализованы в снапшот, они могут быть использованы для повторной инициализации состояния приложения или для повторного получения ресурсов, необходимых приложению, когда приложение перезапускается из снапшота.

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

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

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

v8.startupSnapshot.isBuildingSnapshot()

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

Класс: v8.GCProfiler

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

new v8.GCProfiler()

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

profiler.start()

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

profiler.stop()

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
{
    "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
                    }
                ]
            },
            "стоимость": 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
}

Вот пример.

1
2
3
4
5
6
const { GCProfiler } = require('v8');
const profiler = new GCProfiler();
profiler.start();
setTimeout(() => {
    console.log(profiler.stop());
}, 1000);

Комментарии