Isolate groups in Dart (introduced in version 2.15) allow isolates to share code and some memory structures, significantly increasing the efficiency of communication. Based on my investigation of the Dart SDK's runtime implementation and documentation, here is a summary of what is efficient to send:
When isolates are in the same isolate group (which is the default for isolates created via Isolate.spawn()), the following objects are shared by reference rather than copied:
- All Strings: In the Dart VM, all strings are considered "deeply immutable" and are shared by reference.
- Primitive Types:
bool,int,double, andNull. - Constants: Any object created with the
constkeyword. - Special System Objects:
SendPort,Capability,RegExp,StackTrace, andType. - Deeply Immutable Instances: Objects of classes annotated with
@pragma('vm:deeply-immutable'). - Stateless Closures: Closures that do not capture any mutable state from their environment.
- Unmodifiable Typed Data: If a
TypedData(likeUint8List) is created as unmodifiable (e.g., viaUint8List.unmodifiable()), it can be shared by reference if its backing store is also marked immutable.
TransferableTypedData: This is a specific wrapper for typed data that moves ownership. When you send it, the underlying memory is moved to the receiving isolate, and the sender can no longer access it.Isolate.exit(port, message): This is a very efficient way to return a result. If you callIsolate.exit, themessageobject graph is "reassigned" to the receiving isolate without copying (if they are in the same group), effectively making it a constant-time operation for even huge data structures.
For most other mutable objects (like normal List, Map, or user-defined class instances) sent within the same isolate group:
- The VM performs a "fast copy" by traversing the object graph and allocating new objects directly in the shared heap.
- This is significantly faster than the old "slow copy" (which involved serializing to a byte buffer and deserializing), but it still scales with the size of the object graph.
If you use Isolate.spawnUri(), the new isolate is in a separate isolate group. Communication between different groups always requires full serialization and deserialization (O(N) and much slower).
-
Prefer
const: Use constants whenever possible to ensure$O(1)$ sharing. -
Use
Isolate.exit: When an isolate is finished with its work, useIsolate.exit(resultPort, data)to send back large results for free. -
Use
TransferableTypedData: For large buffers that don't need to be shared, transferring ownership is the most efficient$O(1)$ path. -
Use
@pragma('vm:deeply-immutable'): If you have custom data models that are immutable, this pragma allows the VM to share them by reference.
You can find more technical details in the following files within the repo:
runtime/docs/deeply_immutable.md: Explains how the VM identifies and handles immutable types.runtime/vm/object_graph_copy.cc: The C++ implementation of the fast object copier used within isolate groups.sdk/lib/isolate/isolate.dart: Documentation forSendPort.sendandIsolate.exit.