Skip to content

Isolate Implementation

opencode-agent[bot] edited this page May 9, 2026 · 1 revision

Isolate Implementation

How JNode achieves lightweight process isolation within a single address space using per-isolate static field tables and the @SharedStatics annotation.

Overview

JNode runs all code in a single address space but provides process-like isolation through isolates — lightweight execution contexts with separate static fields. This design allows the VM to avoid the overhead of virtual memory page tables while still providing strong isolation of process-local state. Communication between isolates uses rendezvous-style links.

Key Components

Class / FileRole
core/src/core/org/jnode/vm/isolate/VmIsolate.javaCore isolate implementation — lifecycle, creation, thread startup
core/src/core/org/jnode/vm/isolate/IsolateThread.javaMain thread entry point for a new isolate
core/src/core/org/jnode/vm/isolate/VmStreamBindings.javaRedirectable stdin/stdout/stderr per isolate
core/src/core/org/jnode/vm/isolate/VmLink.javaInter-isolate communication channel
core/src/core/org/jnode/vm/isolate/VmIsolateLocal.javaIsolate-scoped storage (analogous to ThreadLocal)
core/src/classlib/org/jnode/annotation/SharedStatics.javaAnnotation marking classes whose statics are shared across all isolates
core/src/core/org/jnode/vm/memmgr/VmIsolatedStatics.javaPer-isolate static field storage (131,072 slots)
core/src/core/org/jnode/vm/memmgr/VmSharedStatics.javaGlobal static field storage shared by all isolates
core/src/core/org/jnode/vm/scheduler/VmThread.javaThread state including VmIsolatedStatics reference
core/src/core/org/jnode/vm/scheduler/VmProcessor.javaCPU core state including current isolate's statics table
core/src/core/org/jnode/vm/classmgr/VmType.javaClass metadata with separate indices into shared and isolated statics

The Dual-Statics Architecture

JNode maintains two static field tables per class:

┌─────────────────────────────────────────────────────────────┐
│ CLASS METADATA (VmType, in VmSharedStatics) │
│ • Class object reference │
│ • Method code │
│ • TIB, vtable │
├─────────────────────────────────────────────────────────────┤
│ STATIC FIELDS (per-isolate) │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Isolate A's copy │ │ Isolate B's copy │ │
│ │ (VmIsolatedStatics)│ │ (VmIsolatedStatics)│ │
│ └─────────────────────┘ └─────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ STATIC FIELDS (shared, if @SharedStatics) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Single global copy (VmSharedStatics) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

VmSharedStatics

A singleton per VM holding data that must be shared across all isolates:

  • Class objects (VmType instances)
  • Compiled method code
  • String constants
  • Any class annotated with @SharedStatics

Allocated via VmSharedStatics.allocClass(), allocMethodCode(), allocConstantStringField().

VmIsolatedStatics

One instance per isolate, holding that isolate's copy of non-shared static fields. The table has 2^17 (131,072) slots. When a thread enters an isolate, VmThread.switchToIsolate() updates the processor's reference:

publicfinalvoidswitchToIsolate(VmIsolatedStaticsisolatedStatics) {
this.isolatedStatics = isolatedStatics;
VmProcessorproc = VmMagic.currentProcessor();
if (proc.currentThread == this) {
proc.setIsolatedStatics(isolatedStatics);
}
}

VmType Dual Indexing

VmType stores two offsets — one into the shared table and one into the isolated table:

privatefinalintstaticsIndex; // VmSharedStatics for class metadataprivatefinalintisolatedStaticsIndex; // VmIsolatedStatics for instance fields

At compile time, the L1/L2 compilers determine which table to use based on whether the class is marked @SharedStatics.

How @SharedStatics Works

Annotation Definition

@Documented@Retention(CLASS)
@Target(TYPE)
public @interface SharedStatics {
// Marker interface — signals VM that static fields must be shared
}

Processing at Compile Time

The annotation is recognized during class loading in ClassDecoder:

newPragmaAnnotation(SharedStatics.class, TypePragmaFlags.SHAREDSTATICS)

Additionally, several classes are hardcoded as implicitly shared (bypassing the annotation check):

  • java.util.TreeMap
  • org.apache.log4j.LogManager
  • Native VM classes (NativeUnsafe, NativeString, NativeInetAddress)

Runtime Effect

When the JIT compiler generates code to access a static field:

if (!cls.isSharedStatics()) {
// Access from VmIsolatedStatics — per-isolateoffset = getIsolatedStaticsOffset(field);
} else {
// Access from VmSharedStatics — shared across all isolatesoffset = getSharedStaticsOffset(field);
}

This decision is baked into the generated machine code at compile time, so runtime overhead is minimal.

Classes Using @SharedStatics

Over 244 classes are marked @SharedStatics. Key examples:

  • NativeUnsafe, NativeString, NativeInetAddress — native code helpers
  • ResolverImpl, ARPNetworkLayer — network layer singletons
  • JNodeToolkit, SwingToolkit — GUI toolkit internals
  • VmIsolate inner classes: State enum, StaticData, IsolatedStaticData, AppSupport

Isolate Lifecycle

 new VmIsolate(...)
│
▼
CREATED ──────► isolate.start(links)
│ │
▼ ▼
TERMINATED ◄────── STARTING
▲ │
│ ▼
│ RUNNING (main() executes)
│ │
│ ▼
└────────────── EXITING ──► TERMINATED

Creation:

Isolateisolate = newVmIsolate(mainClass, args, bindings, properties);
isolate.start(links);

Current isolate introspection:

VmIsolate.currentIsolate() // Returns the current isolateVmIsolate.isRoot() // Checks if current is the root isolate

Inter-Isolate Communication: Links

Isolates communicate via VmLink — a rendezvous-style message channel:

VmLinklink = VmLink.create();
// Send from isolate Alink.send(newStringLinkMessage("hello"));
// Receive in isolate BStringLinkMessagemsg = (StringLinkMessage) link.receive();

Message types: DataLinkMessage (byte[]), StringLinkMessage, ObjectLinkMessage, LinkLinkMessage, StatusLinkMessage, IsolateLinkMessage.

Gotchas & Non-Obvious Behavior

  • Not virtual memory isolation — Static fields are isolated in software, not by page tables. A bug in the statics indexing code could corrupt another isolate's fields.
  • Code is always shared — Method bytecode and compiled native code live in VmSharedStatics. Only static field values are per-isolate.
  • @SharedStatics is all-or-nothing — A class either has all its static fields shared or all per-isolate. There is no per-field granularity.
  • Root isolate — The kernel runs in the root isolate. Child isolates are created via IsolateThread with the root isolate as parent.
  • Isolate I/O is isolatedVmStreamBindings binds stdin/stdout/stderr per-isolate. Switching isolates via switchToIsolate() does not automatically switch I/O streams — they are set explicitly during isolate startup.
  • ThreadLocal vs IsolateLocalVmIsolateLocal is analogous to ThreadLocal but scoped to the entire isolate. All threads within an isolate see the same value.

Related Pages

Clone this wiki locally