nodejs/node
Environment and Realm
Three C++ types describe the running shape of a Node process: IsolateData, Environment, and Realm. Anywhere in src/, the local Environment* (or Realm*) is the gateway to almost every other subsystem.
IsolateData
IsolateData (declared in src/env.h) holds shared, per-v8::Isolate state. Some of it cannot reasonably live on the Environment: V8 string symbols, snapshot helpers, the platform pointer.
| Member | Holds |
|---|---|
node::ArrayBufferAllocator* |
Allocator used for ArrayBuffers in this isolate. |
| Per-isolate snapshot data | Used to bootstrap the principal Realm from the embedded snapshot. |
| Cached V8 strings and symbols | Like "fd", "length", "data", the symbol names V8 needs. |
worker::WorkerThreadsPerIsolateData |
Pool registry for sibling workers if any. |
There is exactly one IsolateData per v8::Isolate. Workers each have their own isolate and thus their own IsolateData.
Environment
Environment (src/env.h, src/env.cc, ~86K LOC for env.cc and ~45K for env.h) is the central per-thread context.
graph TD
Iso[v8::Isolate] --> IsoData[IsolateData]
IsoData --> Env[Environment]
Env --> Loop[uv_loop_t]
Env --> Opts[per-process + per-Environment Options]
Env --> Insp[InspectorAgent]
Env --> Perf[PerformanceState + histograms]
Env --> Perm[Permission registry]
Env --> Cleanup[CleanupQueue]
Env --> Realms[principal Realm + ShadowRealms]
Env --> Misc[trace state, async-context-frame, source maps, etc.]Things that live on Environment:
- The libuv loop the thread runs.
- The
Options(CLI flags) for this Environment, plus the per-processEnvironmentOptions. - The inspector
Agent. - Performance state, histograms, the eventloop-delay histogram.
- The Permission registry (see Permission Model).
- Cleanup hooks: anything that needs to tear down before the loop closes registers a
RegisterCleanupHook. - The active Realms (one principal Realm plus zero or more ShadowRealms).
- The list of
BaseObjects currently alive that pin the loop alive. - Trace state, source-map cache pointer, async-context-frame state.
Lifecycle
sequenceDiagram
participant Inst as NodeMainInstance
participant Env
participant Realm
participant Loop as libuv
Inst->>Env: Environment::Create
Env->>Realm: PrincipalRealm::CreateMainRealm
Inst->>Env: LoadEnvironment(env, ...)
Env->>Loop: uv_run
Loop-->>Env: callbacks via Realm
Loop->>Env: empty -> exit
Inst->>Env: Environment::Stop, CleanupEnvironment::CleanupHandles waits for libuv handles to close. Environment::RunCleanup walks the cleanup queue. Environment::FreeEnvironment deletes the Environment and its Realms.
Realm
Realm (src/node_realm.h, src/node_realm.cc) is the per-V8-context state. The "principal Realm" is created automatically when an Environment is built; ShadowRealms (Stage-3 TC39) and worker threads have their own Realms too.
What lives on a Realm:
- The V8
Context. - The JS-visible
processobject. - The
internalBinding(name)namespace for this Realm. - Per-Realm
BindingDatastructs (see BaseObject and BindingData). - The
BuiltinModulecache forrequire('node:...'). - Bookkeeping for
process.moduleLoadList.
Bootstrap
Realm::BootstrapRealm() is the one routine that turns a fresh V8 context into a Node-shaped Realm:
- Instantiate per-context primordials by running
lib/internal/per_context/primordials.js. - Instantiate
DOMExceptionandMessagePortJS shims (lib/internal/per_context/domexception.js,messageport.js). - Compile and run
lib/internal/bootstrap/realm.jsto installinternalBinding,BuiltinModule,process.binding. - For the principal Realm, run
lib/internal/bootstrap/node.js(the public bootstrap). - For ShadowRealms, run
lib/internal/bootstrap/shadow_realm.js.
When the embedded snapshot is in use, all of these steps are skipped — the V8 heap is restored from the binary's snapshot blob.
Picking the right type for new code
- If your code is per-thread and per-event-loop, store it on
Environment. - If your code is per-V8-context (per-Realm), store it on
Realm. - If your code is shared across isolates / not Node-specific, it belongs on
IsolateDataor as a free function.
The relevant macros are IsolateData::PER_ISOLATE_* (for isolate-wide caches), Environment::Field accessors, and Realm::PER_REALM_* for per-realm storage.
Integration points
- Snapshots (see Builtins and primordials) operate on
Realm, persisting the V8 heap and walkingBaseObjects reachable from it. - Cleanup is driven by
Environment::CleanupHooks. Subsystems register from theirInitializefunction (Environment::AtExit). - Per-binding storage lives on the Realm via
BindingData.
Entry points for modification
- Adding per-Environment state? Extend
Environment::Fieldenum +inspector.h-style accessor (or useEnvironment::set_xxx()style declared insrc/env.h). - Adding per-Realm state? Use
Realm::AddBindingData<T>()to register aBindingDatasubclass. - Cleanup ordering matters: register a cleanup hook with
Environment::AtExit(cb)so it runs in the Environment teardown sequence.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.