What is the Dart VM?
The Dart VM is a collection of components for executing Dart code natively. Despite its name, the Dart VM is not strictly a “virtual machine” in the traditional sense - it provides an execution environment for Dart but doesn’t always interpret or JIT-compile code. For instance, Dart code can be AOT-compiled to machine code and executed in a stripped-down runtime without any compiler components.The name “Dart VM” is historical. While it provides an execution environment for a high-level programming language, it doesn’t imply that Dart is always interpreted or JIT-compiled.
Core Components
The Dart VM includes the following major components:Runtime System
- Object Model - Defines how Dart objects are represented in memory
- Garbage Collection - Automatic memory management for the heap
- Snapshots - Serialization mechanism for VM state
Development Experience
Components accessible via the service protocol:- Debugging - Interactive debugging support
- Profiling - Performance analysis tools
- Hot-reload - Fast incremental code updates during development
Compilation Pipelines
- Just-in-Time (JIT) - Compiles code during execution
- Ahead-of-Time (AOT) - Pre-compiles code before execution
- Interpreter - Bytecode execution for platforms with restrictions
- ARM simulators - Testing infrastructure
Core Libraries
Native method implementations for Dart’s core libraries (dart:core, dart:async, etc.)
Execution Modes
The Dart VM can execute code in several ways:- From source or Kernel binary using JIT
- From snapshots:
- AOT snapshot (ahead-of-time compiled)
- AppJIT snapshot (JIT-compiled during training run)
The main difference between these modes lies in when and how the VM converts Dart source code to executable code. The runtime environment that facilitates execution remains the same across all modes.
Isolates and Isolate Groups
All Dart code runs within isolates - isolated Dart universes with their own global state and thread of control.Key Characteristics
- Isolation: Each isolate has its own global state and cannot share mutable state with other isolates
- Message Passing: Isolates communicate through message passing via ports
- Isolate Groups: Isolates within a group share the same garbage-collected heap
- Thread Model: An OS thread can enter only one isolate at a time, and only one mutator thread can execute per isolate
Isolates within a group share the same Dart program.
Isolate.spawn creates an isolate within the same group, while Isolate.spawnUri starts a new group.Helper Threads
In addition to the mutator thread, isolates can have helper threads:- Background JIT compiler thread
- GC sweeper threads
- Concurrent GC marker threads
Thread Pool Architecture
The VM uses a thread pool (dart::ThreadPool) to manage OS threads efficiently. Code is structured around task concepts (dart::ThreadPool::Task) rather than explicit OS threads.
For example:
dart::ConcurrentSweeperTask- Posted to thread pool for background GC sweepingdart::MessageHandlerTask- Posted when new messages arrive for isolate message processing
The default implementation of an isolate’s event loop doesn’t spawn a dedicated thread. Instead, it posts a
MessageHandlerTask to the thread pool whenever a new message arrives.From Source to Execution
When you run a Dart program from source:- Common Front-End (CFE) translates Dart source to Kernel AST
- Kernel binary (
.dillfile) is created containing serialized AST - VM loads and executes the Kernel binary
Since Dart 2, the VM no longer executes raw Dart source directly. It requires Kernel binaries (
.dill files) containing serialized Kernel AST.Kernel Service
The standalonedart executable includes a kernel service isolate that handles compilation:
Object Representation
VM objects are split into two parts:Xyzclass (inruntime/vm/object.h) - Defines C++ methodsUntaggedXyzclass (inruntime/vm/raw_object.h) - Defines memory layout
dart::Classanddart::UntaggedClassrepresent a Dart classdart::Fieldanddart::UntaggedFieldrepresent a field within a classdart::Functionanddart::UntaggedFunctionrepresent a function
Lazy Loading from Kernel
When a Kernel binary is loaded, the VM creates objects lazily:- Basic class/library information is loaded first
- Full class details are deserialized only when needed
- Function bodies remain serialized until compilation
Summary
The Dart VM provides a sophisticated runtime environment with:- Multiple execution modes (JIT, AOT, snapshots)
- Isolated execution contexts (isolates and isolate groups)
- Efficient thread management via thread pools
- Lazy loading for fast startup
- A modular architecture supporting different deployment scenarios