Skip to content

V8

소스 코드: lib/v8.js

node:v8 모듈은 Node.js 바이너리에 내장된 V8 버전에 특정한 API를 노출합니다. 다음과 같이 액세스할 수 있습니다.

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

v8.cachedDataVersionTag()

추가된 버전: v8.0.0

V8 버전, 명령줄 플래그 및 감지된 CPU 기능에서 파생된 버전 태그를 나타내는 정수를 반환합니다. 이는 vm.Script cachedData 버퍼가 이 V8 인스턴스와 호환되는지 여부를 결정하는 데 유용합니다.

js
console.log(v8.cachedDataVersionTag()); // 3947234607
// v8.cachedDataVersionTag()에서 반환된 값은 V8 버전,
// 명령줄 플래그 및 감지된 CPU 기능에서 파생됩니다.
// 플래그를 전환할 때 값이 실제로 업데이트되는지 테스트합니다.
v8.setFlagsFromString('--allow_natives_syntax');
console.log(v8.cachedDataVersionTag()); // 183726201

v8.getHeapCodeStatistics()

추가된 버전: v12.8.0

힙의 코드 및 해당 메타데이터에 대한 통계를 가져옵니다. V8 GetHeapCodeAndMetadataStatistics API를 참조하십시오. 다음 속성이 있는 객체를 반환합니다.

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> V8 힙 스냅샷을 포함하는 Readable 스트림입니다.

현재 V8 힙의 스냅샷을 생성하고 JSON 직렬화 표현을 읽는 데 사용할 수 있는 Readable 스트림을 반환합니다. 이 JSON 스트림 형식은 Chrome DevTools와 같은 도구와 함께 사용하기 위한 것입니다. JSON 스키마는 문서화되지 않았으며 V8 엔진에 따라 다릅니다. 따라서 스키마는 V8 버전에 따라 변경될 수 있습니다.

힙 스냅샷을 생성하려면 스냅샷을 생성할 때 힙 크기의 약 두 배에 해당하는 메모리가 필요합니다. 이로 인해 OOM 킬러가 프로세스를 종료할 위험이 있습니다.

스냅샷 생성은 힙 크기에 따라 이벤트 루프를 차단하는 동기 작업입니다.

js
// 힙 스냅샷을 콘솔에 출력합니다.
const v8 = require('node:v8');
const stream = v8.getHeapSnapshot();
stream.pipe(process.stdout);

v8.getHeapSpaceStatistics()

[기록]

버전변경 사항
v7.5.032비트 부호 없는 정수 범위를 초과하는 값을 지원합니다.
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()

[History]

VersionChanges
v7.5.032비트 부호 없는 정수 범위를 초과하는 값 지원.
v7.2.0malloced_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의 값은 V8의 JavaScript 객체에서 현재 사용 중인 바이트 수입니다. 이는 실제로 사용 중인 메모리이며 할당되었지만 아직 사용되지 않은 메모리는 포함하지 않습니다.

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--zap_code_space 옵션이 활성화되었는지 여부를 나타내는 0/1 부울입니다. 이렇게 하면 V8이 힙 가비를 비트 패턴으로 덮어씁니다. RSS footprint(resident set size)는 모든 힙 페이지를 지속적으로 터치하므로 운영 체제에서 스왑될 가능성이 줄어들기 때문에 더 커집니다.

number_of_native_contexts native_context의 값은 현재 활성 상태인 최상위 컨텍스트의 수입니다. 이 숫자가 시간이 지남에 따라 증가하면 메모리 누수를 나타냅니다.

number_of_detached_contexts detached_context의 값은 분리되었지만 아직 가비지 수집되지 않은 컨텍스트의 수입니다. 이 숫자가 0이 아닌 것은 잠재적인 메모리 누수를 나타냅니다.

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

Added in: v22.0.0, v20.13.0

[안정성: 1 - 실험적]

안정성: 1 안정성: 1.1 - 활발한 개발

  • ctor <Function> 힙에서 대상 객체를 필터링하기 위해 프로토타입 체인에서 검색하는 데 사용할 수 있는 생성자입니다.

  • options <undefined> | <Object>

    • format <string> 'count'인 경우 일치하는 객체 수가 반환됩니다. 'summary'인 경우 일치하는 객체의 요약 문자열이 있는 배열이 반환됩니다.
  • 반환: {number|Array

이것은 Chromium DevTools 콘솔에서 제공하는 queryObjects() 콘솔 API와 유사합니다. 메모리 누수 회귀 테스트에 유용할 수 있는 전체 가비지 컬렉션 후 힙에서 프로토타입 체인에 일치하는 생성자가 있는 객체를 검색하는 데 사용할 수 있습니다. 예기치 않은 결과를 피하기 위해 사용자는 구현을 제어하지 않거나 응용 프로그램의 다른 당사자가 호출할 수 있는 생성자에서 이 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이 시작된 후 설정을 변경하면 충돌 및 데이터 손실을 포함하여 예측할 수 없는 동작이 발생할 수 있습니다. 또는 아무런 작업도 수행하지 않을 수 있습니다.

Node.js 버전에서 사용할 수 있는 V8 옵션은 node --v8-options를 실행하여 확인할 수 있습니다.

사용법:

js
// 1분 동안 stdout에 GC 이벤트를 출력합니다.
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}는 Node.js 프로세스의 PID이고 {thread_id}writeHeapSnapshot()이 메인 Node.js 스레드에서 호출되거나 워커 스레드의 ID일 때 0입니다.

  • options <Object>

    • exposeInternals <boolean> true인 경우 힙 스냅샷에 내부 정보를 노출합니다. 기본값: false.
    • exposeNumericValues <boolean> true인 경우 인공 필드에 숫자 값을 노출합니다. 기본값: false.
  • 반환: <string> 스냅샷이 저장된 파일 이름입니다.

현재 V8 힙의 스냅샷을 생성하여 JSON 파일에 씁니다. 이 파일은 Chrome DevTools와 같은 도구와 함께 사용하기 위한 것입니다. JSON 스키마는 문서화되지 않았으며 V8 엔진에 고유하며 V8 버전마다 변경될 수 있습니다.

힙 스냅샷은 단일 V8 격리에 고유합니다. 워커 스레드를 사용하는 경우 메인 스레드에서 생성된 힙 스냅샷에는 워커에 대한 정보가 포함되지 않으며 그 반대도 마찬가지입니다.

힙 스냅샷을 만들려면 스냅샷을 만들 때 힙 크기의 약 두 배의 메모리가 필요합니다. 이로 인해 OOM 킬러가 프로세스를 종료할 위험이 있습니다.

스냅샷 생성은 힙 크기에 따라 이벤트 루프를 차단하는 동기 작업입니다.

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

[안정성: 1 - 실험적]

안정성: 1 안정성: 1 - 실험적

--heapsnapshot-near-heap-limit가 명령줄에서 이미 설정되었거나 API가 두 번 이상 호출된 경우 API는 아무런 작업도 수행하지 않습니다. limit는 양의 정수여야 합니다. 자세한 내용은 --heapsnapshot-near-heap-limit를 참조하십시오.

직렬화 API

직렬화 API는 HTML 구조적 복제 알고리즘과 호환되는 방식으로 JavaScript 값을 직렬화하는 수단을 제공합니다.

형식은 이전 버전과 호환됩니다 (즉, 디스크에 안전하게 저장할 수 있음). 동일한 JavaScript 값이라도 직렬화된 출력이 다를 수 있습니다.

v8.serialize(value)

추가된 버전: v8.0.0

DefaultSerializer를 사용하여 value를 버퍼로 직렬화합니다.

buffer.constants.MAX_LENGTH보다 큰 버퍼를 필요로 하는 거대한 객체를 직렬화하려고 하면 ERR_BUFFER_TOO_LARGE가 발생합니다.

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의 콘텐츠가 대역 외로 전송된 것으로 표시합니다. 역직렬화 컨텍스트에서 해당 ArrayBufferdeserializer.transferArrayBuffer()에 전달합니다.

serializer.writeUint32(value)

원시 32비트 부호 없는 정수를 씁니다. 사용자 정의 serializer._writeHostObject() 내부에서 사용합니다.

serializer.writeUint64(hi, lo)

높음 및 낮음 32비트 부분으로 분할된 원시 64비트 부호 없는 정수를 씁니다. 사용자 정의 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

추가된 버전: v8.0.0

new Deserializer(buffer)

새로운 Deserializer 객체를 생성합니다.

deserializer.readHeader()

헤더(포맷 버전 포함)를 읽고 유효성을 검사합니다. 예를 들어, 유효하지 않거나 지원되지 않는 와이어 포맷을 거부할 수 있습니다. 이 경우 Error가 발생합니다.

deserializer.readValue()

버퍼에서 JavaScript 값을 역직렬화하여 반환합니다.

deserializer.transferArrayBuffer(id, arrayBuffer)

ArrayBuffer의 내용이 대역 외로 전송되었음을 표시합니다. 직렬화 컨텍스트에서 해당 ArrayBufferserializer.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)

deserializer의 내부 버퍼에서 원시 바이트를 읽습니다. length 매개변수는 serializer.writeRawBytes()에 전달된 버퍼의 길이에 해당해야 합니다. 사용자 정의 deserializer._readHostObject() 내에서 사용됩니다.

deserializer._readHostObject()

이 메서드는 일부 종류의 호스트 객체, 즉 네이티브 C++ 바인딩으로 생성된 객체를 읽기 위해 호출됩니다. 데이터를 역직렬화할 수 없는 경우 적절한 예외를 발생시켜야 합니다.

이 메서드는 Deserializer 클래스 자체에는 없지만 하위 클래스에서 제공할 수 있습니다.

클래스: v8.DefaultSerializer

다음 버전부터 추가됨: v8.0.0

Serializer의 서브클래스로, TypedArray(특히 Buffer) 및 DataView 객체를 호스트 객체로 직렬화하고, 해당 객체가 참조하는 기본 ArrayBuffer의 일부만 저장합니다.

클래스: v8.DefaultDeserializer

다음 버전부터 추가됨: v8.0.0

DefaultSerializer에 의해 작성된 형식에 해당하는 Deserializer의 서브클래스입니다.

Promise 훅

promiseHooks 인터페이스를 사용하여 프로미스 수명 주기 이벤트를 추적할 수 있습니다. 모든 비동기 활동을 추적하려면 async_hooks를 참조하십시오. 이 모듈은 내부적으로 이 모듈을 사용하여 다른 비동기 리소스에 대한 이벤트 외에도 프로미스 수명 주기 이벤트를 생성합니다. 요청 컨텍스트 관리에 대해서는 AsyncLocalStorage를 참조하십시오.

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

// 프로미스에서 생성되는 네 가지 수명 주기 이벤트가 있습니다.

// `init` 이벤트는 프로미스의 생성을 나타냅니다. 이는 `new Promise(...)`와 같은
// 직접적인 생성일 수도 있고, `then()` 또는 `catch()`와 같은 연속일 수도 있습니다.
// 또한 비동기 함수가 호출되거나 `await`를 수행할 때마다 발생합니다. 연속 프로미스가
// 생성되면 `parent`는 연속의 대상이 되는 프로미스가 됩니다.
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('프로미스가 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 훅은 일반 함수여야 합니다. 비동기 함수를 제공하면 무한 마이크로태스크 루프가 발생하므로 예외가 발생합니다.

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 훅은 일반 함수여야 합니다. 비동기 함수를 제공하면 무한 마이크로태스크 루프가 발생하므로 예외가 발생합니다.

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 훅은 일반 함수여야 합니다. 비동기 함수를 제공하면 무한 마이크로태스크 루프가 발생하므로 예외가 발생합니다.

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 Hook은 일반 함수여야 합니다. Async 함수를 제공하면 무한 마이크로태스크 루프를 생성하므로 오류가 발생합니다.

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

Hook 콜백은 일반 함수여야 합니다. Async 함수를 제공하면 무한 마이크로태스크 루프를 생성하므로 오류가 발생합니다.

각 Promise의 수명 주기 이벤트에 대해 호출될 함수를 등록합니다.

Promise 수명 주기 동안 각각의 이벤트에 대해 init()/before()/after()/settled() 콜백이 호출됩니다.

모든 콜백은 선택 사항입니다. 예를 들어 Promise 생성만 추적해야 하는 경우 init 콜백만 전달하면 됩니다. callbacks에 전달할 수 있는 모든 함수의 구체적인 내용은 Hook 콜백 섹션에 있습니다.

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) {},
});

Hook 콜백

프로미스의 수명 주기에서 발생하는 주요 이벤트는 프로미스 생성, 연속 핸들러 호출 전/후 또는 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() 핸들러 후 또는 다른 awaitawait 전에 발생할 수 있습니다.

settled(promise)

프로미스가 확인 또는 거부 값을 받을 때 호출됩니다. 이는 Promise.resolve() 또는 Promise.reject()의 경우 동기적으로 발생할 수 있습니다.

Startup Snapshot API

추가된 버전: v18.6.0, v16.17.0

[안정성: 1 - 실험적]

안정성: 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.jsv8.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> 직렬화하기 전에 호출될 콜백입니다.
  • data <any> 호출될 때 callback에 전달될 선택적 데이터입니다.

Node.js 인스턴스가 스냅샷으로 직렬화되어 종료되기 직전에 호출될 콜백을 추가합니다. 이는 직렬화되어서는 안 되거나 직렬화할 수 없는 리소스를 해제하거나 사용자 데이터를 직렬화에 더 적합한 형태로 변환하는 데 사용할 수 있습니다.

콜백은 추가된 순서대로 실행됩니다.

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

추가된 버전: v18.6.0, v16.17.0

  • callback <Function> 스냅샷이 역직렬화된 후 호출될 콜백입니다.
  • data <any> 호출될 때 callback에 전달될 선택적 데이터입니다.

Node.js 인스턴스가 스냅샷에서 역직렬화될 때 호출될 콜백을 추가합니다. callbackdata(제공된 경우)는 스냅샷으로 직렬화되며 애플리케이션의 상태를 다시 초기화하거나 애플리케이션이 스냅샷에서 다시 시작될 때 애플리케이션에 필요한 리소스를 다시 획득하는 데 사용할 수 있습니다.

콜백은 추가된 순서대로 실행됩니다.

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

추가된 버전: v18.6.0, v16.17.0

  • callback <Function> 스냅샷이 역직렬화된 후 진입점으로 호출될 콜백입니다.
  • data <any> 호출될 때 callback에 전달될 선택적 데이터입니다.

스냅샷에서 역직렬화될 때 Node.js 애플리케이션의 진입점을 설정합니다. 이는 스냅샷 빌드 스크립트에서 한 번만 호출할 수 있습니다. 호출되면 역직렬화된 애플리케이션은 더 이상 시작하기 위한 추가 진입점 스크립트가 필요하지 않으며 역직렬화된 데이터(제공된 경우)와 함께 콜백을 호출합니다. 그렇지 않으면 진입점 스크립트를 역직렬화된 애플리케이션에 여전히 제공해야 합니다.

v8.startupSnapshot.isBuildingSnapshot()

Added in: v18.6.0, v16.17.0

Node.js 인스턴스가 스냅샷을 빌드하기 위해 실행되는 경우 true를 반환합니다.

Class: v8.GCProfiler

Added in: v19.6.0, v18.15.0

이 API는 현재 스레드에서 GC 데이터를 수집합니다.

new v8.GCProfiler()

Added in: v19.6.0, v18.15.0

v8.GCProfiler 클래스의 새 인스턴스를 생성합니다.

profiler.start()

Added in: v19.6.0, v18.15.0

GC 데이터 수집을 시작합니다.

profiler.stop()

Added in: 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);