Version 2.12.0-197.0.dev

Merge commit '0a63d23c11fa9617b4c4a254a0d1c56d00f3aeee' into 'dev'
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
index 59cbd3c..3d12c45 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
@@ -147,8 +147,6 @@
     if (isNonNullableByDefault) {
       if (contextType is! NeverType &&
           type1 is! NeverType &&
-          isSubtypeOf(contextType, coreTypes.numNonNullableRawType,
-              SubtypeCheckMode.withNullabilities) &&
           isSubtypeOf(type1, coreTypes.numNonNullableRawType,
               SubtypeCheckMode.withNullabilities)) {
         // If e is an expression of the form e1 + e2, e1 - e2, e1 * e2, e1 % e2
diff --git a/pkg/front_end/testcases/nnbd/issue44595.dart b/pkg/front_end/testcases/nnbd/issue44595.dart
new file mode 100644
index 0000000..a5997d4
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44595.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async";
+
+T id<T>(T value) => value;
+
+main() async {
+  FutureOr<int> x = 1 + id(1); // Should work, gives error.
+
+  // Checking it!
+  FutureOr<int> y = 1 + id(1)
+    ..checkStaticType<Exactly<int>>();
+  FutureOr<int> z = 1 + contextType(1)
+    ..checkStaticType<Exactly<int>>();
+}
+
+extension<T> on T {
+  void checkStaticType<R extends Exactly<T>>() {}
+}
+
+typedef Exactly<T> = T Function(T);
+T contextType<T>(Object? o) => o as T;
diff --git a/pkg/front_end/testcases/nnbd/issue44595.dart.outline.expect b/pkg/front_end/testcases/nnbd/issue44595.dart.outline.expect
new file mode 100644
index 0000000..59b9de9
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44595.dart.outline.expect
@@ -0,0 +1,21 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+typedef Exactly<invariant T extends dynamic = dynamic> = (T%) → T%;
+extension _extension#0<T extends core::Object? = dynamic> on T% {
+  method checkStaticType = self::_extension#0|checkStaticType;
+  tearoff checkStaticType = self::_extension#0|get#checkStaticType;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% value) → self::id::T%
+  ;
+static method main() → dynamic async 
+  ;
+static method _extension#0|checkStaticType<T extends core::Object? = dynamic, R extends (self::_extension#0|checkStaticType::T%) → self::_extension#0|checkStaticType::T% = (dynamic) → dynamic>(lowered final self::_extension#0|checkStaticType::T% #this) → void
+  ;
+static method _extension#0|get#checkStaticType<T extends core::Object? = dynamic>(lowered final self::_extension#0|get#checkStaticType::T% #this) → <R extends (self::_extension#0|get#checkStaticType::T%) → self::_extension#0|get#checkStaticType::T% = (dynamic) → dynamic>() → void
+  return <R extends (self::_extension#0|get#checkStaticType::T%) → self::_extension#0|get#checkStaticType::T% = (dynamic) → dynamic>() → void => self::_extension#0|checkStaticType<self::_extension#0|get#checkStaticType::T%, R>(#this);
+static method contextType<T extends core::Object? = dynamic>(core::Object? o) → self::contextType::T%
+  ;
diff --git a/pkg/front_end/testcases/nnbd/issue44595.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue44595.dart.strong.expect
new file mode 100644
index 0000000..2c6d5bc
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44595.dart.strong.expect
@@ -0,0 +1,27 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+typedef Exactly<invariant T extends dynamic = dynamic> = (T%) → T%;
+extension _extension#0<T extends core::Object? = dynamic> on T% {
+  method checkStaticType = self::_extension#0|checkStaticType;
+  tearoff checkStaticType = self::_extension#0|get#checkStaticType;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% value) → self::id::T%
+  return value;
+static method main() → dynamic async {
+  FutureOr<core::int>x = 1.{core::num::+}(self::id<core::int>(1));
+  FutureOr<core::int>y = let final core::int #t1 = 1.{core::num::+}(self::id<core::int>(1)) in block {
+    self::_extension#0|checkStaticType<core::int, (core::int) → core::int>(#t1);
+  } =>#t1;
+  FutureOr<core::int>z = let final core::int #t2 = 1.{core::num::+}(self::contextType<core::int>(1)) in block {
+    self::_extension#0|checkStaticType<core::int, (core::int) → core::int>(#t2);
+  } =>#t2;
+}
+static method _extension#0|checkStaticType<T extends core::Object? = dynamic, R extends (self::_extension#0|checkStaticType::T%) → self::_extension#0|checkStaticType::T% = (dynamic) → dynamic>(lowered final self::_extension#0|checkStaticType::T% #this) → void {}
+static method _extension#0|get#checkStaticType<T extends core::Object? = dynamic>(lowered final self::_extension#0|get#checkStaticType::T% #this) → <R extends (self::_extension#0|get#checkStaticType::T%) → self::_extension#0|get#checkStaticType::T% = (dynamic) → dynamic>() → void
+  return <R extends (self::_extension#0|get#checkStaticType::T%) → self::_extension#0|get#checkStaticType::T% = (dynamic) → dynamic>() → void => self::_extension#0|checkStaticType<self::_extension#0|get#checkStaticType::T%, R>(#this);
+static method contextType<T extends core::Object? = dynamic>(core::Object? o) → self::contextType::T%
+  return o as{ForNonNullableByDefault} self::contextType::T%;
diff --git a/pkg/front_end/testcases/nnbd/issue44595.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue44595.dart.strong.transformed.expect
new file mode 100644
index 0000000..5861f8c
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44595.dart.strong.transformed.expect
@@ -0,0 +1,51 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Exactly<invariant T extends dynamic = dynamic> = (T%) → T%;
+extension _extension#0<T extends core::Object? = dynamic> on T% {
+  method checkStaticType = self::_extension#0|checkStaticType;
+  tearoff checkStaticType = self::_extension#0|get#checkStaticType;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% value) → self::id::T%
+  return value;
+static method main() → dynamic /* originally async */ {
+  final asy::_Future<dynamic> :async_future = new asy::_Future::•<dynamic>();
+  core::bool* :is_sync = false;
+  FutureOr<dynamic>? :return_value;
+  (dynamic) → dynamic :async_op_then;
+  (core::Object, core::StackTrace) → dynamic :async_op_error;
+  core::int :await_jump_var = 0;
+  dynamic :await_ctx_var;
+  function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
+    try {
+      #L1:
+      {
+        FutureOr<core::int>x = 1.{core::num::+}(self::id<core::int>(1));
+        FutureOr<core::int>y = let final core::int #t1 = 1.{core::num::+}(self::id<core::int>(1)) in block {
+          self::_extension#0|checkStaticType<core::int, (core::int) → core::int>(#t1);
+        } =>#t1;
+        FutureOr<core::int>z = let final core::int #t2 = 1.{core::num::+}(self::contextType<core::int>(1)) in block {
+          self::_extension#0|checkStaticType<core::int, (core::int) → core::int>(#t2);
+        } =>#t2;
+      }
+      asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
+      return;
+    }
+    on dynamic catch(dynamic exception, core::StackTrace stack_trace) {
+      asy::_completeOnAsyncError(:async_future, exception, stack_trace, :is_sync);
+    }
+  :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+  :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+  :async_op.call();
+  :is_sync = true;
+  return :async_future;
+}
+static method _extension#0|checkStaticType<T extends core::Object? = dynamic, R extends (self::_extension#0|checkStaticType::T%) → self::_extension#0|checkStaticType::T% = (dynamic) → dynamic>(lowered final self::_extension#0|checkStaticType::T% #this) → void {}
+static method _extension#0|get#checkStaticType<T extends core::Object? = dynamic>(lowered final self::_extension#0|get#checkStaticType::T% #this) → <R extends (self::_extension#0|get#checkStaticType::T%) → self::_extension#0|get#checkStaticType::T% = (dynamic) → dynamic>() → void
+  return <R extends (self::_extension#0|get#checkStaticType::T%) → self::_extension#0|get#checkStaticType::T% = (dynamic) → dynamic>() → void => self::_extension#0|checkStaticType<self::_extension#0|get#checkStaticType::T%, R>(#this);
+static method contextType<T extends core::Object? = dynamic>(core::Object? o) → self::contextType::T%
+  return o as{ForNonNullableByDefault} self::contextType::T%;
diff --git a/pkg/front_end/testcases/nnbd/issue44595.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue44595.dart.textual_outline.expect
new file mode 100644
index 0000000..219eb6e
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44595.dart.textual_outline.expect
@@ -0,0 +1,11 @@
+import "dart:async";
+
+T id<T>(T value) => value;
+main() async {}
+
+extension<T> on T {
+  void checkStaticType<R extends Exactly<T>>() {}
+}
+
+typedef Exactly<T> = T Function(T);
+T contextType<T>(Object? o) => o as T;
diff --git a/pkg/front_end/testcases/nnbd/issue44595.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue44595.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..70d1eb0
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44595.dart.textual_outline_modelled.expect
@@ -0,0 +1,11 @@
+import "dart:async";
+
+T contextType<T>(Object? o) => o as T;
+T id<T>(T value) => value;
+
+extension<T> on T {
+  void checkStaticType<R extends Exactly<T>>() {}
+}
+
+main() async {}
+typedef Exactly<T> = T Function(T);
diff --git a/pkg/front_end/testcases/nnbd/issue44595.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue44595.dart.weak.expect
new file mode 100644
index 0000000..2c6d5bc
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44595.dart.weak.expect
@@ -0,0 +1,27 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "dart:async";
+
+typedef Exactly<invariant T extends dynamic = dynamic> = (T%) → T%;
+extension _extension#0<T extends core::Object? = dynamic> on T% {
+  method checkStaticType = self::_extension#0|checkStaticType;
+  tearoff checkStaticType = self::_extension#0|get#checkStaticType;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% value) → self::id::T%
+  return value;
+static method main() → dynamic async {
+  FutureOr<core::int>x = 1.{core::num::+}(self::id<core::int>(1));
+  FutureOr<core::int>y = let final core::int #t1 = 1.{core::num::+}(self::id<core::int>(1)) in block {
+    self::_extension#0|checkStaticType<core::int, (core::int) → core::int>(#t1);
+  } =>#t1;
+  FutureOr<core::int>z = let final core::int #t2 = 1.{core::num::+}(self::contextType<core::int>(1)) in block {
+    self::_extension#0|checkStaticType<core::int, (core::int) → core::int>(#t2);
+  } =>#t2;
+}
+static method _extension#0|checkStaticType<T extends core::Object? = dynamic, R extends (self::_extension#0|checkStaticType::T%) → self::_extension#0|checkStaticType::T% = (dynamic) → dynamic>(lowered final self::_extension#0|checkStaticType::T% #this) → void {}
+static method _extension#0|get#checkStaticType<T extends core::Object? = dynamic>(lowered final self::_extension#0|get#checkStaticType::T% #this) → <R extends (self::_extension#0|get#checkStaticType::T%) → self::_extension#0|get#checkStaticType::T% = (dynamic) → dynamic>() → void
+  return <R extends (self::_extension#0|get#checkStaticType::T%) → self::_extension#0|get#checkStaticType::T% = (dynamic) → dynamic>() → void => self::_extension#0|checkStaticType<self::_extension#0|get#checkStaticType::T%, R>(#this);
+static method contextType<T extends core::Object? = dynamic>(core::Object? o) → self::contextType::T%
+  return o as{ForNonNullableByDefault} self::contextType::T%;
diff --git a/pkg/front_end/testcases/nnbd/issue44595.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue44595.dart.weak.transformed.expect
new file mode 100644
index 0000000..5861f8c
--- /dev/null
+++ b/pkg/front_end/testcases/nnbd/issue44595.dart.weak.transformed.expect
@@ -0,0 +1,51 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+typedef Exactly<invariant T extends dynamic = dynamic> = (T%) → T%;
+extension _extension#0<T extends core::Object? = dynamic> on T% {
+  method checkStaticType = self::_extension#0|checkStaticType;
+  tearoff checkStaticType = self::_extension#0|get#checkStaticType;
+}
+static method id<T extends core::Object? = dynamic>(self::id::T% value) → self::id::T%
+  return value;
+static method main() → dynamic /* originally async */ {
+  final asy::_Future<dynamic> :async_future = new asy::_Future::•<dynamic>();
+  core::bool* :is_sync = false;
+  FutureOr<dynamic>? :return_value;
+  (dynamic) → dynamic :async_op_then;
+  (core::Object, core::StackTrace) → dynamic :async_op_error;
+  core::int :await_jump_var = 0;
+  dynamic :await_ctx_var;
+  function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding 
+    try {
+      #L1:
+      {
+        FutureOr<core::int>x = 1.{core::num::+}(self::id<core::int>(1));
+        FutureOr<core::int>y = let final core::int #t1 = 1.{core::num::+}(self::id<core::int>(1)) in block {
+          self::_extension#0|checkStaticType<core::int, (core::int) → core::int>(#t1);
+        } =>#t1;
+        FutureOr<core::int>z = let final core::int #t2 = 1.{core::num::+}(self::contextType<core::int>(1)) in block {
+          self::_extension#0|checkStaticType<core::int, (core::int) → core::int>(#t2);
+        } =>#t2;
+      }
+      asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
+      return;
+    }
+    on dynamic catch(dynamic exception, core::StackTrace stack_trace) {
+      asy::_completeOnAsyncError(:async_future, exception, stack_trace, :is_sync);
+    }
+  :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+  :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+  :async_op.call();
+  :is_sync = true;
+  return :async_future;
+}
+static method _extension#0|checkStaticType<T extends core::Object? = dynamic, R extends (self::_extension#0|checkStaticType::T%) → self::_extension#0|checkStaticType::T% = (dynamic) → dynamic>(lowered final self::_extension#0|checkStaticType::T% #this) → void {}
+static method _extension#0|get#checkStaticType<T extends core::Object? = dynamic>(lowered final self::_extension#0|get#checkStaticType::T% #this) → <R extends (self::_extension#0|get#checkStaticType::T%) → self::_extension#0|get#checkStaticType::T% = (dynamic) → dynamic>() → void
+  return <R extends (self::_extension#0|get#checkStaticType::T%) → self::_extension#0|get#checkStaticType::T% = (dynamic) → dynamic>() → void => self::_extension#0|checkStaticType<self::_extension#0|get#checkStaticType::T%, R>(#this);
+static method contextType<T extends core::Object? = dynamic>(core::Object? o) → self::contextType::T%
+  return o as{ForNonNullableByDefault} self::contextType::T%;
diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc
index cefe5a0..f0ccd17 100644
--- a/runtime/lib/ffi.cc
+++ b/runtime/lib/ffi.cc
@@ -222,7 +222,7 @@
   }
 
   const auto& typed_data_class =
-      Class::Handle(zone, isolate->class_table()->At(cid));
+      Class::Handle(zone, isolate->group()->class_table()->At(cid));
   const auto& error =
       Error::Handle(zone, typed_data_class.EnsureIsAllocateFinalized(thread));
   if (!error.IsNull()) {
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 85286cb..23352fc 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -163,7 +163,7 @@
   if (!obj.raw()->IsHeapObject() || obj.raw()->ptr()->IsCanonical()) {
     return obj.raw();
   }
-  ClassTable* class_table = isolate->class_table();
+  ClassTable* class_table = isolate->group()->class_table();
 
   Class& klass = Class::Handle(zone);
   Closure& closure = Closure::Handle(zone);
@@ -457,7 +457,7 @@
 
 ObjectPtr IsolateSpawnState::ResolveFunction() {
   Thread* thread = Thread::Current();
-  Isolate* I = thread->isolate();
+  auto IG = thread->isolate_group();
   Zone* zone = thread->zone();
 
   const String& func_name = String::Handle(zone, String::New(function_name()));
@@ -466,7 +466,7 @@
     // Handle spawnUri lookup rules.
     // Check whether the root library defines a main function.
     const Library& lib =
-        Library::Handle(zone, I->object_store()->root_library());
+        Library::Handle(zone, IG->object_store()->root_library());
     Function& func = Function::Handle(zone, lib.LookupLocalFunction(func_name));
     if (func.IsNull()) {
       // Check whether main is reexported from the root library.
@@ -934,7 +934,7 @@
 
   // Canonicalize the uri with respect to the current isolate.
   const Library& root_lib =
-      Library::Handle(isolate->object_store()->root_library());
+      Library::Handle(isolate->group()->object_store()->root_library());
   char* error = NULL;
   const char* canonical_uri = CanonicalizeUri(thread, root_lib, uri, &error);
   if (canonical_uri == NULL) {
@@ -988,7 +988,7 @@
 
 DEFINE_NATIVE_ENTRY(Isolate_getCurrentRootUriStr, 0, 0) {
   const Library& root_lib =
-      Library::Handle(zone, isolate->object_store()->root_library());
+      Library::Handle(zone, isolate->group()->object_store()->root_library());
   return root_lib.url();
 }
 
@@ -1071,8 +1071,8 @@
 
   uint8_t* data = reinterpret_cast<uint8_t*>(::malloc(total_bytes));
   if (data == nullptr) {
-    const Instance& exception =
-        Instance::Handle(thread->isolate()->object_store()->out_of_memory());
+    const Instance& exception = Instance::Handle(
+        thread->isolate_group()->object_store()->out_of_memory());
     Exceptions::Throw(thread, exception);
     UNREACHABLE();
   }
@@ -1122,7 +1122,7 @@
   const ExternalTypedData& typed_data = ExternalTypedData::Handle(
       ExternalTypedData::New(kExternalTypedDataUint8ArrayCid, data, length,
                              thread->heap()->SpaceForExternal(length)));
-  FinalizablePersistentHandle::New(thread->isolate(), typed_data,
+  FinalizablePersistentHandle::New(thread->isolate_group(), typed_data,
                                    /* peer= */ data,
                                    &ExternalTypedDataFinalizer, length,
                                    /*auto_delete=*/true);
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index a3ca6fe..4575352 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -585,8 +585,8 @@
   Thread* thread = Thread::Current();
   Isolate* isolate = thread->isolate();
   const String& debug_name = String::Handle(String::New(isolate->name()));
-  const Library& root_library =
-      Library::Handle(thread->zone(), isolate->object_store()->root_library());
+  const Library& root_library = Library::Handle(
+      thread->zone(), isolate->group()->object_store()->root_library());
   const Instance& root_library_mirror =
       Instance::Handle(CreateLibraryMirror(thread, root_library));
 
@@ -649,8 +649,8 @@
 }
 
 DEFINE_NATIVE_ENTRY(MirrorSystem_libraries, 0, 0) {
-  const GrowableObjectArray& libraries =
-      GrowableObjectArray::Handle(zone, isolate->object_store()->libraries());
+  const GrowableObjectArray& libraries = GrowableObjectArray::Handle(
+      zone, isolate->group()->object_store()->libraries());
 
   const intptr_t num_libraries = libraries.Length();
   const GrowableObjectArray& library_mirrors = GrowableObjectArray::Handle(
@@ -696,11 +696,11 @@
   } else {
     isolate->BlockClassFinalization();
     const Object& result = Object::Handle(
-        zone,
-        isolate->CallTagHandler(
-            Dart_kCanonicalizeUrl,
-            Library::Handle(zone, isolate->object_store()->root_library()),
-            uri));
+        zone, isolate->CallTagHandler(
+                  Dart_kCanonicalizeUrl,
+                  Library::Handle(
+                      zone, isolate->group()->object_store()->root_library()),
+                  uri));
     isolate->UnblockClassFinalization();
     if (result.IsError()) {
       if (result.IsLanguageError()) {
@@ -726,7 +726,8 @@
   Object& result = Object::Handle(
       zone, isolate->CallTagHandler(
                 Dart_kImportTag,
-                Library::Handle(zone, isolate->object_store()->root_library()),
+                Library::Handle(
+                    zone, isolate->group()->object_store()->root_library()),
                 canonical_uri));
   isolate->UnblockClassFinalization();
   if (result.IsError()) {
@@ -1289,8 +1290,8 @@
       // The 'instantiator' created below should not be a type, but two type
       // argument vectors: instantiator_type_arguments and
       // function_type_arguments.
-      const Class& cls =
-          Class::Handle(Isolate::Current()->object_store()->object_class());
+      const Class& cls = Class::Handle(
+          IsolateGroup::Current()->object_store()->object_class());
       instantiator = Type::New(cls, arguments, TokenPosition::kNoSource);
       instantiator.SetIsFinalized();
     }
diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc
index 4e92dc7..2cee6cf 100644
--- a/runtime/lib/object.cc
+++ b/runtime/lib/object.cc
@@ -40,7 +40,7 @@
 #if defined(HASH_IN_OBJECT_HEADER)
   return Smi::New(Object::GetCachedHash(arguments->NativeArgAt(0)));
 #else
-  Heap* heap = isolate->heap();
+  Heap* heap = isolate->group()->heap();
   ASSERT(arguments->NativeArgAt(0)->IsDartInstance());
   return Smi::New(heap->GetHash(arguments->NativeArgAt(0)));
 #endif
@@ -53,7 +53,7 @@
 #else
   const Instance& instance =
       Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
-  Heap* heap = isolate->heap();
+  Heap* heap = isolate->group()->heap();
   heap->SetHash(instance.raw(), hash.Value());
 #endif
   return Object::null();
@@ -221,7 +221,8 @@
 
 DEFINE_NATIVE_ENTRY(LibraryPrefix_issueLoad, 0, 1) {
   const Smi& id = Smi::CheckedHandle(zone, arguments->NativeArgAt(0));
-  Array& units = Array::Handle(zone, isolate->object_store()->loading_units());
+  Array& units =
+      Array::Handle(zone, isolate->group()->object_store()->loading_units());
   if (units.IsNull()) {
     // Not actually split.
     const Library& lib = Library::Handle(zone, Library::CoreLibrary());
@@ -259,7 +260,7 @@
 }
 
 DEFINE_NATIVE_ENTRY(Internal_collectAllGarbage, 0, 0) {
-  isolate->heap()->CollectAllGarbage();
+  isolate->group()->heap()->CollectAllGarbage();
   return Object::null();
 }
 
diff --git a/runtime/lib/string.cc b/runtime/lib/string.cc
index 170bc96..631f3ad 100644
--- a/runtime/lib/string.cc
+++ b/runtime/lib/string.cc
@@ -282,7 +282,7 @@
                                           Heap::kNew);
   result.Add(str);
   result.SetTypeArguments(TypeArguments::Handle(
-      zone, isolate->object_store()->type_argument_string()));
+      zone, isolate->group()->object_store()->type_argument_string()));
   return result.raw();
 }
 
@@ -292,8 +292,8 @@
   if ((length < 0) || (length > OneByteString::kMaxElements)) {
     // Assume that negative lengths are the result of wrapping in code in
     // string_patch.dart.
-    const Instance& exception =
-        Instance::Handle(thread->isolate()->object_store()->out_of_memory());
+    const Instance& exception = Instance::Handle(
+        thread->isolate_group()->object_store()->out_of_memory());
     Exceptions::Throw(thread, exception);
     UNREACHABLE();
   }
@@ -306,8 +306,8 @@
   if ((length < 0) || (length > TwoByteString::kMaxElements)) {
     // Assume that negative lengths are the result of wrapping in code in
     // string_patch.dart.
-    const Instance& exception =
-        Instance::Handle(thread->isolate()->object_store()->out_of_memory());
+    const Instance& exception = Instance::Handle(
+        thread->isolate_group()->object_store()->out_of_memory());
     Exceptions::Throw(thread, exception);
     UNREACHABLE();
   }
diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc
index d99a1b9..4662ff3 100644
--- a/runtime/vm/bootstrap.cc
+++ b/runtime/vm/bootstrap.cc
@@ -48,7 +48,7 @@
   // Eagerly compile the _Closure class as it is the class of all closure
   // instances. This allows us to just finalize function types without going
   // through the hoops of trying to compile their scope class.
-  ObjectStore* object_store = thread->isolate()->object_store();
+  ObjectStore* object_store = thread->isolate_group()->object_store();
   Zone* zone = thread->zone();
   Class& cls = Class::Handle(zone, object_store->closure_class());
   cls.EnsureIsFinalized(thread);
@@ -111,7 +111,6 @@
   if (setjmp(*jump.Set()) == 0) {
     kernel::KernelLoader loader(program.get(), /*uri_to_source_table=*/nullptr);
 
-    auto isolate = thread->isolate();
     auto isolate_group = thread->isolate_group();
 
     if (isolate_group->obfuscate()) {
@@ -122,7 +121,7 @@
     Library& library = Library::Handle(zone);
     for (intptr_t i = 0; i < kBootstrapLibraryCount; ++i) {
       ObjectStore::BootstrapLibraryId id = bootstrap_libraries[i].index;
-      library = isolate->object_store()->bootstrap_library(id);
+      library = isolate_group->object_store()->bootstrap_library(id);
       loader.LoadLibrary(library);
     }
 
@@ -141,7 +140,7 @@
     const auto& dart_builtin =
         String::Handle(zone, String::New("dart:_builtin"));
     library = Library::LookupLibrary(thread, dart_builtin);
-    isolate->object_store()->set_builtin_library(library);
+    isolate_group->object_store()->set_builtin_library(library);
 
     if (FLAG_precompiled_mode) {
       loader.ReadLoadingUnits();
@@ -158,7 +157,7 @@
 ErrorPtr Bootstrap::DoBootstrapping(const uint8_t* kernel_buffer,
                                     intptr_t kernel_buffer_size) {
   Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   Zone* zone = thread->zone();
   String& uri = String::Handle(zone);
   Library& lib = Library::Handle(zone);
@@ -169,13 +168,13 @@
   for (intptr_t i = 0; i < kBootstrapLibraryCount; ++i) {
     ObjectStore::BootstrapLibraryId id = bootstrap_libraries[i].index;
     uri = Symbols::New(thread, bootstrap_libraries[i].uri);
-    lib = isolate->object_store()->bootstrap_library(id);
+    lib = isolate_group->object_store()->bootstrap_library(id);
     ASSERT(lib.raw() == Library::LookupLibrary(thread, uri));
     if (lib.IsNull()) {
       lib = Library::NewLibraryHelper(uri, false);
       lib.SetLoadRequested();
       lib.Register(thread);
-      isolate->object_store()->set_bootstrap_library(id, lib);
+      isolate_group->object_store()->set_bootstrap_library(id, lib);
     }
   }
 
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index a26ecbb..4af9266 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -30,7 +30,7 @@
 DEFINE_FLAG(bool, trace_type_finalization, false, "Trace type finalization.");
 
 bool ClassFinalizer::AllClassesFinalized() {
-  ObjectStore* object_store = Isolate::Current()->object_store();
+  ObjectStore* object_store = IsolateGroup::Current()->object_store();
   const GrowableObjectArray& classes =
       GrowableObjectArray::Handle(object_store->pending_classes());
   return classes.Length() == 0;
@@ -48,7 +48,7 @@
     return;
   }
   // Switch all functions' code to unoptimized.
-  const ClassTable& class_table = *Isolate::Current()->class_table();
+  const ClassTable& class_table = *IsolateGroup::Current()->class_table();
   Class& cls = Class::Handle();
   for (intptr_t i = 0; i < added_subclass_to_cids.length(); i++) {
     intptr_t cid = added_subclass_to_cids[i];
@@ -175,10 +175,10 @@
 bool ClassFinalizer::ProcessPendingClasses() {
   Thread* thread = Thread::Current();
   TIMELINE_DURATION(thread, Isolate, "ProcessPendingClasses");
-  Isolate* isolate = thread->isolate();
-  ASSERT(isolate != NULL);
+  auto isolate_group = thread->isolate_group();
+  ASSERT(isolate_group != nullptr);
   HANDLESCOPE(thread);
-  ObjectStore* object_store = isolate->object_store();
+  ObjectStore* object_store = isolate_group->object_store();
   const Error& error = Error::Handle(thread->zone(), thread->sticky_error());
   if (!error.IsNull()) {
     return false;
@@ -225,7 +225,7 @@
   if (FLAG_trace_class_finalization) {
     OS::PrintErr("VerifyBootstrapClasses START.\n");
   }
-  ObjectStore* object_store = Isolate::Current()->object_store();
+  ObjectStore* object_store = IsolateGroup::Current()->object_store();
 
   Class& cls = Class::Handle();
 #if defined(DEBUG)
@@ -284,7 +284,7 @@
   if (FLAG_trace_class_finalization) {
     OS::PrintErr("VerifyBootstrapClasses END.\n");
   }
-  Isolate::Current()->heap()->Verify();
+  IsolateGroup::Current()->heap()->Verify();
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
@@ -934,13 +934,13 @@
   //   instance method.
 
   // Finalize type of fields and check for conflicts in super classes.
-  Isolate* isolate = Isolate::Current();
+  auto isolate_group = IsolateGroup::Current();
   Zone* zone = Thread::Current()->zone();
   Array& array = Array::Handle(zone, cls.fields());
   Field& field = Field::Handle(zone);
   AbstractType& type = AbstractType::Handle(zone);
   const intptr_t num_fields = array.Length();
-  const bool track_exactness = isolate->use_field_guards();
+  const bool track_exactness = isolate_group->use_field_guards();
   for (intptr_t i = 0; i < num_fields; i++) {
     field ^= array.At(i);
     type = field.type();
@@ -1178,7 +1178,7 @@
     }
 
     Zone* zone = thread->zone();
-    ClassTable* class_table = thread->isolate()->class_table();
+    ClassTable* class_table = thread->isolate_group()->class_table();
     auto& interface_class = Class::Handle(zone);
 
     // We scan every interface this [cls] implements and invalidate all CHA
@@ -1380,16 +1380,17 @@
 }
 
 void ClassFinalizer::SortClasses() {
-  Thread* T = Thread::Current();
-  Zone* Z = T->zone();
-  Isolate* I = T->isolate();
+  auto T = Thread::Current();
+  auto Z = T->zone();
+  auto I = T->isolate();
+  auto IG = T->isolate_group();
 
   // Prevent background compiler from adding deferred classes or canonicalizing
   // new types while classes are being sorted and type hashes are modified.
   BackgroundCompiler::Stop(I);
   SafepointWriteRwLocker ml(T, T->isolate_group()->program_lock());
 
-  ClassTable* table = I->class_table();
+  ClassTable* table = IG->class_table();
   intptr_t num_cids = table->NumCids();
 
   std::unique_ptr<intptr_t[]> old_to_new_cid(new intptr_t[num_cids]);
@@ -1415,7 +1416,7 @@
     if (!cls.is_declaration_loaded()) {
       continue;
     }
-    if (cls.SuperClass() == I->object_store()->object_class()) {
+    if (cls.SuperClass() == IG->object_store()->object_class()) {
       dfs_stack.Add(cid);
     }
   }
@@ -1527,12 +1528,11 @@
     IG->ForEachIsolate(
         [&](Isolate* I) {
           I->set_remapping_cids(true);
-
-          // Update the class table. Do it before rewriting cids in headers, as
-          // the heap walkers load an object's size *after* calling the visitor.
-          I->class_table()->Remap(old_to_new_cid);
         },
         /*is_at_safepoint=*/true);
+    // Update the class table. Do it before rewriting cids in headers, as
+    // the heap walkers load an object's size *after* calling the visitor.
+    IG->class_table()->Remap(old_to_new_cid);
 
     // Rewrite cids in headers and cids in Classes, Fields, Types and
     // TypeParameters.
@@ -1544,11 +1544,11 @@
     IG->ForEachIsolate(
         [&](Isolate* I) {
           I->set_remapping_cids(false);
-#if defined(DEBUG)
-          I->class_table()->Validate();
-#endif
         },
         /*is_at_safepoint=*/true);
+#if defined(DEBUG)
+    IG->class_table()->Validate();
+#endif
   }
 
 #if defined(DEBUG)
@@ -1621,19 +1621,20 @@
 };
 
 void ClassFinalizer::RehashTypes() {
-  Thread* T = Thread::Current();
-  Zone* Z = T->zone();
-  Isolate* I = T->isolate();
+  auto T = Thread::Current();
+  auto Z = T->zone();
+  auto I = T->isolate();
+  auto IG = T->isolate_group();
 
   // Clear all cached hash values.
   {
     HeapIterationScope his(T);
     ClearTypeHashVisitor visitor(Z);
-    I->heap()->VisitObjects(&visitor);
+    IG->heap()->VisitObjects(&visitor);
   }
 
   // Rehash the canonical Types table.
-  ObjectStore* object_store = I->object_store();
+  ObjectStore* object_store = IG->object_store();
   Array& types = Array::Handle(Z);
   Type& type = Type::Handle(Z);
   {
@@ -1704,7 +1705,7 @@
   UNREACHABLE();
 #else
   auto const thread = Thread::Current();
-  auto const isolate = thread->isolate();
+  auto const isolate_group = thread->isolate_group();
   StackZone stack_zone(thread);
   HANDLESCOPE(thread);
   auto const zone = thread->zone();
@@ -1734,12 +1735,12 @@
   };
 
   ClearCodeVisitor visitor(zone, including_nonchanging_cids);
-  ProgramVisitor::WalkProgram(zone, isolate, &visitor);
+  ProgramVisitor::WalkProgram(zone, isolate_group, &visitor);
 
   // Apart from normal function code and allocation stubs we have two global
   // code objects to clear.
   if (including_nonchanging_cids) {
-    auto object_store = isolate->object_store();
+    auto object_store = isolate_group->object_store();
     auto& null_code = Code::Handle(zone);
     object_store->set_build_method_extractor_code(null_code);
   }
diff --git a/runtime/vm/class_finalizer_test.cc b/runtime/vm/class_finalizer_test.cc
index 7f91581..62c85a1 100644
--- a/runtime/vm/class_finalizer_test.cc
+++ b/runtime/vm/class_finalizer_test.cc
@@ -25,9 +25,9 @@
 
 ISOLATE_UNIT_TEST_CASE(ClassFinalizer) {
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
-  ObjectStore* object_store = isolate->object_store();
-  const GrowableObjectArray& pending_classes =
+  auto isolate_group = thread->isolate_group();
+  ObjectStore* object_store = isolate_group->object_store();
+  const auto& pending_classes =
       GrowableObjectArray::Handle(zone, object_store->pending_classes());
   GrowableArray<const Class*> classes_1;
   classes_1.Add(&Class::Handle(CreateTestClass("BMW")));
diff --git a/runtime/vm/class_table.cc b/runtime/vm/class_table.cc
index 582ff56..b182553 100644
--- a/runtime/vm/class_table.cc
+++ b/runtime/vm/class_table.cc
@@ -32,8 +32,7 @@
         calloc(capacity_, sizeof(RelaxedAtomic<intptr_t>))));
   } else {
     // Duplicate the class table from the VM isolate.
-    auto vm_shared_class_table =
-        Dart::vm_isolate()->group()->shared_class_table();
+    auto vm_shared_class_table = Dart::vm_isolate_group()->shared_class_table();
     capacity_ = vm_shared_class_table->capacity_;
     // Note that [calloc] will zero-initialize the memory.
     RelaxedAtomic<intptr_t>* table = reinterpret_cast<RelaxedAtomic<intptr_t>*>(
@@ -98,7 +97,7 @@
     table_.store(static_cast<ClassPtr*>(calloc(capacity_, sizeof(ClassPtr))));
   } else {
     // Duplicate the class table from the VM isolate.
-    ClassTable* vm_class_table = Dart::vm_isolate()->class_table();
+    ClassTable* vm_class_table = Dart::vm_isolate_group()->class_table();
     capacity_ = vm_class_table->capacity_;
     // Note that [calloc] will zero-initialize the memory.
     ClassPtr* table =
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index a4ecb71..31c8d4f 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -293,7 +293,7 @@
     predefined_start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     intptr_t count = d->ReadUnsigned();
-    ClassTable* table = d->isolate()->class_table();
+    ClassTable* table = d->isolate_group()->class_table();
     for (intptr_t i = 0; i < count; i++) {
       intptr_t class_id = d->ReadCid();
       ASSERT(table->HasValidClassAt(class_id));
@@ -312,7 +312,7 @@
   }
 
   void ReadFill(Deserializer* d, bool is_canonical) {
-    ClassTable* table = d->isolate()->class_table();
+    ClassTable* table = d->isolate_group()->class_table();
 
     for (intptr_t id = predefined_start_index_; id < predefined_stop_index_;
          id++) {
@@ -497,7 +497,8 @@
   void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
       CanonicalTypeArgumentsSet table(
-          d->zone(), d->isolate()->object_store()->canonical_type_arguments());
+          d->zone(),
+          d->isolate_group()->object_store()->canonical_type_arguments());
       TypeArguments& type_arg = TypeArguments::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         type_arg ^= refs.At(i);
@@ -507,7 +508,7 @@
         // equal.
         ASSERT(!present || type_arg.IsRecursive());
       }
-      d->isolate()->object_store()->set_canonical_type_arguments(
+      d->isolate_group()->object_store()->set_canonical_type_arguments(
           table.Release());
     }
   }
@@ -1134,7 +1135,7 @@
 
   void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     Field& field = Field::Handle(d->zone());
-    if (!Isolate::Current()->use_field_guards()) {
+    if (!IsolateGroup::Current()->use_field_guards()) {
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         field ^= refs.At(i);
         field.set_guarded_cid_unsafe(kDynamicCid);
@@ -2321,8 +2322,8 @@
   void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     if (is_canonical && IsStringClassId(cid_) &&
         (d->isolate() != Dart::vm_isolate())) {
-      CanonicalStringSet table(d->zone(),
-                               d->isolate()->object_store()->symbol_table());
+      CanonicalStringSet table(
+          d->zone(), d->isolate_group()->object_store()->symbol_table());
       String& str = String::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         str ^= refs.At(i);
@@ -2330,7 +2331,7 @@
         bool present = table.Insert(str);
         ASSERT(!present);
       }
-      d->isolate()->object_store()->set_symbol_table(table.Release());
+      d->isolate_group()->object_store()->set_symbol_table(table.Release());
     }
   }
 
@@ -3079,7 +3080,7 @@
  public:
   explicit InstanceSerializationCluster(intptr_t cid)
       : SerializationCluster("Instance"), cid_(cid) {
-    ClassPtr cls = Isolate::Current()->class_table()->At(cid);
+    ClassPtr cls = IsolateGroup::Current()->class_table()->At(cid);
     host_next_field_offset_in_words_ =
         cls->ptr()->host_next_field_offset_in_words_;
     ASSERT(host_next_field_offset_in_words_ > 0);
@@ -3322,7 +3323,7 @@
 
     SmiPtr raw_type_class_id = Smi::RawCast(type->ptr()->type_class_id_);
     ClassPtr type_class =
-        s->isolate()->class_table()->At(Smi::Value(raw_type_class_id));
+        s->isolate_group()->class_table()->At(Smi::Value(raw_type_class_id));
     s->Push(type_class);
   }
 
@@ -3394,8 +3395,8 @@
 
   void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
-      CanonicalTypeSet table(d->zone(),
-                             d->isolate()->object_store()->canonical_types());
+      CanonicalTypeSet table(
+          d->zone(), d->isolate_group()->object_store()->canonical_types());
       Type& type = Type::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         type ^= refs.At(i);
@@ -3405,7 +3406,7 @@
         // equal.
         ASSERT(!present || type.IsRecursive());
       }
-      d->isolate()->object_store()->set_canonical_types(table.Release());
+      d->isolate_group()->object_store()->set_canonical_types(table.Release());
     }
 
     Type& type = Type::Handle(d->zone());
@@ -3596,7 +3597,8 @@
   void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
       CanonicalTypeParameterSet table(
-          d->zone(), d->isolate()->object_store()->canonical_type_parameters());
+          d->zone(),
+          d->isolate_group()->object_store()->canonical_type_parameters());
       TypeParameter& type_param = TypeParameter::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         type_param ^= refs.At(i);
@@ -3606,7 +3608,7 @@
           ASSERT(!present);
         }
       }
-      d->isolate()->object_store()->set_canonical_type_parameters(
+      d->isolate_group()->object_store()->set_canonical_type_parameters(
           table.Release());
     }
 
@@ -3764,7 +3766,7 @@
   void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
       const Class& mint_cls = Class::Handle(
-          d->zone(), Isolate::Current()->object_store()->mint_class());
+          d->zone(), IsolateGroup::Current()->object_store()->mint_class());
       mint_cls.set_constants(Object::null_array());
       Object& number = Object::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
@@ -4627,8 +4629,8 @@
 
   void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
-      CanonicalStringSet table(d->zone(),
-                               d->isolate()->object_store()->symbol_table());
+      CanonicalStringSet table(
+          d->zone(), d->isolate_group()->object_store()->symbol_table());
       String& str = String::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         str ^= refs.At(i);
@@ -4636,7 +4638,7 @@
         bool present = table.Insert(str);
         ASSERT(!present);
       }
-      d->isolate()->object_store()->set_symbol_table(table.Release());
+      d->isolate_group()->object_store()->set_symbol_table(table.Release());
     }
   }
 };
@@ -4721,8 +4723,8 @@
 
   void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
-      CanonicalStringSet table(d->zone(),
-                               d->isolate()->object_store()->symbol_table());
+      CanonicalStringSet table(
+          d->zone(), d->isolate_group()->object_store()->symbol_table());
       String& str = String::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         str ^= refs.At(i);
@@ -4730,7 +4732,7 @@
         bool present = table.Insert(str);
         ASSERT(!present);
       }
-      d->isolate()->object_store()->set_symbol_table(table.Release());
+      d->isolate_group()->object_store()->set_symbol_table(table.Release());
     }
   }
 };
@@ -4805,7 +4807,7 @@
     s->AddBaseObject(SubtypeTestCache::cached_array_, "Array",
                      "<empty subtype entries>");
 
-    ClassTable* table = s->isolate()->class_table();
+    ClassTable* table = s->isolate_group()->class_table();
     for (intptr_t cid = kClassCid; cid < kInstanceCid; cid++) {
       // Error, CallSiteData has no class object.
       if (cid != kErrorCid && cid != kCallSiteDataCid) {
@@ -4885,7 +4887,7 @@
     }
     d->AddBaseObject(SubtypeTestCache::cached_array_);
 
-    ClassTable* table = d->isolate()->class_table();
+    ClassTable* table = d->isolate_group()->class_table();
     for (intptr_t cid = kClassCid; cid <= kUnwindErrorCid; cid++) {
       // Error, CallSiteData has no class object.
       if (cid != kErrorCid && cid != kCallSiteDataCid) {
@@ -4905,7 +4907,7 @@
 
   void ReadRoots(Deserializer* d) {
     symbol_table_ ^= d->ReadRef();
-    d->isolate()->object_store()->set_symbol_table(symbol_table_);
+    d->isolate_group()->object_store()->set_symbol_table(symbol_table_);
     if (Snapshot::IncludesCode(d->kind())) {
       for (intptr_t i = 0; i < StubCode::NumEntries(); i++) {
         Code* code = Code::ReadOnlyHandle();
@@ -4920,7 +4922,7 @@
     // allocations (e.g., FinalizeVMIsolate) before allocating new pages.
     d->heap()->old_space()->AbandonBumpAllocation();
 
-    Symbols::InitFromSnapshot(d->isolate());
+    Symbols::InitFromSnapshot(d->isolate_group());
 
     Object::set_vm_isolate_snapshot_object_table(refs);
   }
@@ -5063,12 +5065,13 @@
   }
 
   void PostLoad(Deserializer* d, const Array& refs) {
-    Isolate* isolate = d->thread()->isolate();
-    isolate->class_table()->CopySizesFromClassObjects();
+    auto isolate = d->thread()->isolate();
+    auto isolate_group = d->thread()->isolate_group();
+    isolate_group->class_table()->CopySizesFromClassObjects();
     d->heap()->old_space()->EvaluateAfterLoading();
 
     const Array& units =
-        Array::Handle(isolate->object_store()->loading_units());
+        Array::Handle(isolate_group->object_store()->loading_units());
     if (!units.IsNull()) {
       LoadingUnit& unit = LoadingUnit::Handle();
       unit ^= units.At(LoadingUnit::kRootId);
@@ -5208,7 +5211,7 @@
                        bool vm,
                        V8SnapshotProfileWriter* profile_writer)
     : ThreadStackResource(thread),
-      heap_(thread->isolate()->heap()),
+      heap_(thread->isolate_group()->heap()),
       zone_(thread->zone()),
       kind_(kind),
       stream_(stream),
@@ -5235,8 +5238,8 @@
       deduped_instructions_sources_(zone_)
 #endif
 {
-  num_cids_ = thread->isolate()->class_table()->NumCids();
-  num_tlc_cids_ = thread->isolate()->class_table()->NumTopLevelCids();
+  num_cids_ = thread->isolate_group()->class_table()->NumCids();
+  num_tlc_cids_ = thread->isolate_group()->class_table()->NumTopLevelCids();
   canonical_clusters_by_cid_ = new SerializationCluster*[num_cids_];
   for (intptr_t i = 0; i < num_cids_; i++) {
     canonical_clusters_by_cid_[i] = nullptr;
@@ -5461,7 +5464,7 @@
 #else
   Zone* Z = zone_;
   if (cid >= kNumPredefinedCids || cid == kInstanceCid) {
-    Push(isolate()->class_table()->At(cid));
+    Push(isolate_group()->class_table()->At(cid));
     return new (Z) InstanceSerializationCluster(cid);
   }
   if (IsTypedDataViewClassId(cid)) {
@@ -6092,7 +6095,8 @@
     // snapshot, we always serialize at least _one_ byte for the DispatchTable.
     if (dispatch_table_size_ > 0) {
       const auto& dispatch_table_entries = Array::Handle(
-          zone_, isolate()->object_store()->dispatch_table_code_entries());
+          zone_,
+          isolate_group()->object_store()->dispatch_table_code_entries());
       auto const entry_count =
           dispatch_table_entries.IsNull() ? 0 : dispatch_table_entries.Length();
       clusters_by_size.Add(new (zone_) FakeSerializationCluster(
@@ -6123,7 +6127,7 @@
                            bool is_non_root_unit,
                            intptr_t offset)
     : ThreadStackResource(thread),
-      heap_(thread->isolate()->heap()),
+      heap_(thread->isolate_group()->heap()),
       zone_(thread->zone()),
       kind_(kind),
       stream_(buffer, size),
@@ -6287,7 +6291,8 @@
   const intptr_t first_code_id = stream->ReadUnsigned();
 
   auto const I = isolate();
-  auto code = I->object_store()->dispatch_table_null_error_stub();
+  auto const IG = isolate_group();
+  auto code = IG->object_store()->dispatch_table_null_error_stub();
   ASSERT(code != Code::null());
   uword null_entry = Code::EntryPointOf(code);
 
@@ -6544,7 +6549,7 @@
       code = refs.At(start_index + i);
       order_table.SetAt(i, code);
     }
-    ObjectStore* object_store = Isolate::Current()->object_store();
+    ObjectStore* object_store = IsolateGroup::Current()->object_store();
     GrowableObjectArray& order_tables =
         GrowableObjectArray::Handle(zone_, object_store->code_order_tables());
     if (order_tables.IsNull()) {
@@ -6665,7 +6670,7 @@
   Isolate* isolate = thread()->isolate();
   isolate->ValidateClassTable();
   if (isolate != Dart::vm_isolate()) {
-    isolate->heap()->Verify();
+    isolate->group()->heap()->Verify();
   }
 #endif
 
@@ -6707,7 +6712,7 @@
       mapped_text_size_(0) {
   ASSERT(isolate() != NULL);
   ASSERT(heap() != NULL);
-  ObjectStore* object_store = isolate()->object_store();
+  ObjectStore* object_store = isolate_group()->object_store();
   ASSERT(object_store != NULL);
 
 #if defined(DEBUG)
@@ -6734,7 +6739,7 @@
   serializer.ReserveHeader();
   serializer.WriteVersionAndFeatures(true);
   VMSerializationRoots roots(
-      Array::Handle(Dart::vm_isolate()->object_store()->symbol_table()));
+      Array::Handle(Dart::vm_isolate_group()->object_store()->symbol_table()));
   ZoneGrowableArray<Object*>* objects = serializer.Serialize(&roots);
   serializer.FillHeader(serializer.kind());
   clustered_vm_size_ = serializer.bytes_written();
@@ -6763,7 +6768,7 @@
                         isolate_image_writer_, /*vm=*/false, profile_writer_);
   serializer.set_loading_units(units);
   serializer.set_current_loading_unit_id(LoadingUnit::kRootId);
-  ObjectStore* object_store = isolate()->object_store();
+  ObjectStore* object_store = isolate_group()->object_store();
   ASSERT(object_store != NULL);
 
   // These type arguments must always be retained.
@@ -7102,7 +7107,7 @@
                                        /* is_executable */ true);
   }
 
-  ProgramDeserializationRoots roots(thread_->isolate()->object_store());
+  ProgramDeserializationRoots roots(thread_->isolate_group()->object_store());
   deserializer.Deserialize(&roots);
 
   PatchGlobalObjectPool();
@@ -7129,7 +7134,7 @@
   }
   {
     Array& units =
-        Array::Handle(thread_->isolate()->object_store()->loading_units());
+        Array::Handle(isolate_group()->object_store()->loading_units());
     uint32_t main_program_hash = Smi::Value(Smi::RawCast(units.At(0)));
     uint32_t unit_program_hash = deserializer.Read<uint32_t>();
     if (main_program_hash != unit_program_hash) {
@@ -7172,7 +7177,7 @@
     auto zone = thread_->zone();
     const auto& pool = ObjectPool::Handle(
         zone, ObjectPool::RawCast(
-                  thread_->isolate()->object_store()->global_object_pool()));
+                  isolate_group()->object_store()->global_object_pool()));
     auto& entry = Object::Handle(zone);
     auto& smi = Smi::Handle(zone);
     for (intptr_t i = 0; i < pool.Length(); i++) {
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index 2b2ab85..07a7b1e 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -675,7 +675,8 @@
   Thread* thread() const { return thread_; }
   Zone* zone() const { return thread_->zone(); }
   Isolate* isolate() const { return thread_->isolate(); }
-  Heap* heap() const { return isolate()->heap(); }
+  IsolateGroup* isolate_group() const { return thread_->isolate_group(); }
+  Heap* heap() const { return isolate_group()->heap(); }
 
   // Writes a full snapshot of the program(VM isolate, regular isolate group).
   void WriteFullSnapshot(
@@ -727,6 +728,8 @@
   ApiErrorPtr ReadUnitSnapshot(const LoadingUnit& unit);
 
  private:
+  IsolateGroup* isolate_group() const { return thread_->isolate_group(); }
+
   ApiErrorPtr ConvertToApiError(char* message);
   void PatchGlobalObjectPool();
   void InitializeBSS();
diff --git a/runtime/vm/code_descriptors_test.cc b/runtime/vm/code_descriptors_test.cc
index 1f5cb52..2fd788b 100644
--- a/runtime/vm/code_descriptors_test.cc
+++ b/runtime/vm/code_descriptors_test.cc
@@ -27,7 +27,7 @@
   EXPECT_EQ(20, value);
   {
     TransitionNativeToVM transition(Thread::Current());
-    Isolate::Current()->heap()->CollectAllGarbage();
+    IsolateGroup::Current()->heap()->CollectAllGarbage();
   }
 }
 
diff --git a/runtime/vm/code_patcher_x64.cc b/runtime/vm/code_patcher_x64.cc
index 0077394..e918bfdd 100644
--- a/runtime/vm/code_patcher_x64.cc
+++ b/runtime/vm/code_patcher_x64.cc
@@ -335,7 +335,7 @@
   BareSwitchableCall(uword return_address, const Code& code)
       : SwitchableCallBase(code) {
     object_pool_ = ObjectPool::RawCast(
-        Isolate::Current()->object_store()->global_object_pool());
+        IsolateGroup::Current()->object_store()->global_object_pool());
 
     uword pc = return_address;
 
@@ -401,7 +401,7 @@
     if (result != Code::null()) {
       return result;
     }
-    result = ReversePc::Lookup(Dart::vm_isolate()->group(), pc);
+    result = ReversePc::Lookup(Dart::vm_isolate_group(), pc);
     if (result != Code::null()) {
       return result;
     }
diff --git a/runtime/vm/compilation_trace.cc b/runtime/vm/compilation_trace.cc
index 8b1a53b..412ee128 100644
--- a/runtime/vm/compilation_trace.cc
+++ b/runtime/vm/compilation_trace.cc
@@ -112,8 +112,8 @@
   // Next, compile common dispatchers. These aren't found with the normal
   // lookup above because they have irregular lookup that depends on the
   // arguments descriptor (e.g. call() versus call(x)).
-  const Class& closure_class =
-      Class::Handle(zone_, thread_->isolate()->object_store()->closure_class());
+  const Class& closure_class = Class::Handle(
+      zone_, thread_->isolate_group()->object_store()->closure_class());
   Array& arguments_descriptor = Array::Handle(zone_);
   Function& dispatcher = Function::Handle(zone_);
   for (intptr_t argc = 1; argc <= 4; argc++) {
@@ -134,7 +134,7 @@
   // Finally, compile closures in all compiled functions. Don't cache the
   // length since compiling may append to this list.
   const GrowableObjectArray& closure_functions = GrowableObjectArray::Handle(
-      zone_, thread_->isolate()->object_store()->closure_functions());
+      zone_, thread_->isolate_group()->object_store()->closure_functions());
   for (intptr_t i = 0; i < closure_functions.Length(); i++) {
     function_ ^= closure_functions.At(i);
     function2_ = function_.parent_function();
@@ -401,14 +401,15 @@
     if (static_type_.IsNull()) {
       continue;
     } else if (static_type_.IsDoubleType()) {
-      receiver_cls_ = Isolate::Current()->class_table()->At(kDoubleCid);
+      receiver_cls_ = IsolateGroup::Current()->class_table()->At(kDoubleCid);
     } else if (static_type_.IsIntType()) {
-      receiver_cls_ = Isolate::Current()->class_table()->At(kSmiCid);
+      receiver_cls_ = IsolateGroup::Current()->class_table()->At(kSmiCid);
     } else if (static_type_.IsStringType()) {
-      receiver_cls_ = Isolate::Current()->class_table()->At(kOneByteStringCid);
+      receiver_cls_ =
+          IsolateGroup::Current()->class_table()->At(kOneByteStringCid);
     } else if (static_type_.IsDartFunctionType() ||
                static_type_.IsDartClosureType()) {
-      receiver_cls_ = Isolate::Current()->class_table()->At(kClosureCid);
+      receiver_cls_ = IsolateGroup::Current()->class_table()->At(kClosureCid);
     } else if (static_type_.HasTypeClass()) {
       receiver_cls_ = static_type_.type_class();
       if (receiver_cls_.is_implemented() || receiver_cls_.is_abstract()) {
@@ -470,7 +471,7 @@
 }
 
 void TypeFeedbackSaver::SaveClasses() {
-  ClassTable* table = Isolate::Current()->class_table();
+  ClassTable* table = IsolateGroup::Current()->class_table();
 
   intptr_t num_cids = table->NumCids();
   WriteInt(num_cids);
@@ -482,7 +483,7 @@
 }
 
 void TypeFeedbackSaver::SaveFields() {
-  ClassTable* table = Isolate::Current()->class_table();
+  ClassTable* table = IsolateGroup::Current()->class_table();
   intptr_t num_cids = table->NumCids();
   for (intptr_t cid = kNumPredefinedCids; cid < num_cids; cid++) {
     cls_ = table->At(cid);
@@ -890,7 +891,7 @@
 
       intptr_t reuse_index = call_site_.FindCheck(cids);
       if (reuse_index == -1) {
-        cls_ = thread_->isolate()->class_table()->At(cids[0]);
+        cls_ = thread_->isolate_group()->class_table()->At(cids[0]);
         // Use target name and args descriptor from the current program
         // instead of saved feedback to get the correct private mangling and
         // ensure no arity mismatch crashes.
@@ -965,7 +966,7 @@
     // been unoptimized compilated and the child function created and added to
     // ObjectStore::closure_functions_.
     const GrowableObjectArray& closure_functions = GrowableObjectArray::Handle(
-        zone_, thread_->isolate()->object_store()->closure_functions());
+        zone_, thread_->isolate_group()->object_store()->closure_functions());
     bool found = false;
     for (intptr_t i = 0; i < closure_functions.Length(); i++) {
       func_ ^= closure_functions.At(i);
diff --git a/runtime/vm/compiler/aot/aot_call_specializer.cc b/runtime/vm/compiler/aot/aot_call_specializer.cc
index ddfc55b..efc9d68 100644
--- a/runtime/vm/compiler/aot/aot_call_specializer.cc
+++ b/runtime/vm/compiler/aot/aot_call_specializer.cc
@@ -38,7 +38,7 @@
             "generate exhaustive class tests instead of a megamorphic call");
 
 // Quick access to the current isolate and zone.
-#define I (isolate())
+#define IG (isolate_group())
 #define Z (zone())
 
 #ifdef DART_PRECOMPILER
@@ -49,15 +49,15 @@
 //   the receiver type can be only the function's class.
 // Returns Function::null() if there is no unique dynamic target for
 // given 'fname'. 'fname' must be a symbol.
-static void GetUniqueDynamicTarget(Isolate* isolate,
+static void GetUniqueDynamicTarget(IsolateGroup* isolate_group,
                                    const String& fname,
                                    Object* function) {
   UniqueFunctionsMap functions_map(
-      isolate->object_store()->unique_dynamic_targets());
+      isolate_group->object_store()->unique_dynamic_targets());
   ASSERT(fname.IsSymbol());
   *function = functions_map.GetOrNull(fname);
   ASSERT(functions_map.Release().raw() ==
-         isolate->object_store()->unique_dynamic_targets());
+         isolate_group->object_store()->unique_dynamic_targets());
 }
 
 AotCallSpecializer::AotCallSpecializer(
@@ -70,8 +70,9 @@
       precompiler_(precompiler),
       has_unique_no_such_method_(false) {
   Function& target_function = Function::Handle();
-  if (isolate()->object_store()->unique_dynamic_targets() != Array::null()) {
-    GetUniqueDynamicTarget(isolate(), Symbols::NoSuchMethod(),
+  if (isolate_group()->object_store()->unique_dynamic_targets() !=
+      Array::null()) {
+    GetUniqueDynamicTarget(isolate_group(), Symbols::NoSuchMethod(),
                            &target_function);
     has_unique_no_such_method_ = !target_function.IsNull();
   }
@@ -79,13 +80,15 @@
 
 bool AotCallSpecializer::TryCreateICDataForUniqueTarget(
     InstanceCallInstr* call) {
-  if (isolate()->object_store()->unique_dynamic_targets() == Array::null()) {
+  if (isolate_group()->object_store()->unique_dynamic_targets() ==
+      Array::null()) {
     return false;
   }
 
   // Check if the target is unique.
   Function& target_function = Function::Handle(Z);
-  GetUniqueDynamicTarget(isolate(), call->function_name(), &target_function);
+  GetUniqueDynamicTarget(isolate_group(), call->function_name(),
+                         &target_function);
 
   if (target_function.IsNull()) {
     return false;
@@ -158,7 +161,7 @@
 
   // There is only a single function Object.get:runtimeType that can be invoked
   // by this call. Convert dynamic invocation to a static one.
-  const Class& cls = Class::Handle(Z, I->object_store()->object_class());
+  const Class& cls = Class::Handle(Z, IG->object_store()->object_class());
   const Function& function =
       Function::Handle(Z, call->ResolveForReceiverClass(cls));
   ASSERT(!function.IsNull());
@@ -192,7 +195,7 @@
 
   if (IsGetRuntimeType(left) && left->input_use_list()->IsSingleUse() &&
       IsGetRuntimeType(right) && right->input_use_list()->IsSingleUse()) {
-    const Class& cls = Class::Handle(Z, I->object_store()->object_class());
+    const Class& cls = Class::Handle(Z, IG->object_store()->object_class());
     const Function& have_same_runtime_type = Function::ZoneHandle(
         Z,
         cls.LookupStaticFunctionAllowPrivate(Symbols::HaveSameRuntimeType()));
@@ -870,7 +873,7 @@
       instr->ArgumentValueAt(receiver_idx)->Type()->ToCid();
   if (receiver_cid != kDynamicCid) {
     const Class& receiver_class =
-        Class::Handle(Z, isolate()->class_table()->At(receiver_cid));
+        Class::Handle(Z, isolate_group()->class_table()->At(receiver_cid));
     const Function& function =
         Function::Handle(Z, instr->ResolveForReceiverClass(receiver_class));
     if (!function.IsNull()) {
@@ -893,7 +896,8 @@
         // Skip sentinel cid. It may appear in the unreachable code after
         // inlining a method which doesn't return.
         if (cid == kNeverCid) continue;
-        const Class& cls = Class::Handle(Z, isolate()->class_table()->At(cid));
+        const Class& cls =
+            Class::Handle(Z, isolate_group()->class_table()->At(cid));
         const Function& target =
             Function::Handle(Z, instr->ResolveForReceiverClass(cls));
         if (target.recognized_kind() != MethodRecognizer::kObjectEquals) {
@@ -952,7 +956,7 @@
       Class& cls = Class::Handle(Z);
       for (intptr_t i = 0; i < class_ids.length(); i++) {
         const intptr_t cid = class_ids[i];
-        cls = isolate()->class_table()->At(cid);
+        cls = isolate_group()->class_table()->At(cid);
         target = instr->ResolveForReceiverClass(cls);
         ASSERT(target.IsNull() || !target.IsInvokeFieldDispatcher());
         if (target.IsNull()) {
@@ -1163,7 +1167,7 @@
       call->ArgumentValueAt(receiver_idx)->Type()->ToCid();
   if (receiver_cid != kDynamicCid) {
     const Class& receiver_class =
-        Class::Handle(Z, isolate()->class_table()->At(receiver_cid));
+        Class::Handle(Z, isolate_group()->class_table()->At(receiver_cid));
     const Function& function =
         Function::ZoneHandle(Z, call->ResolveForReceiverClass(receiver_class));
     if (!function.IsNull()) {
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 37a4d1b..4d4fcc5 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -163,7 +163,8 @@
       dropped_typearg_count_(0),
       dropped_type_count_(0),
       dropped_library_count_(0),
-      libraries_(GrowableObjectArray::Handle(I->object_store()->libraries())),
+      libraries_(GrowableObjectArray::Handle(
+          isolate_->group()->object_store()->libraries())),
       pending_functions_(
           GrowableObjectArray::Handle(GrowableObjectArray::New())),
       sent_selectors_(),
@@ -229,7 +230,7 @@
 
       if (FLAG_use_bare_instructions && FLAG_use_table_dispatch) {
         dispatch_table_generator_ = new compiler::DispatchTableGenerator(Z);
-        dispatch_table_generator_->Initialize(I->class_table());
+        dispatch_table_generator_->Initialize(IG->class_table());
       }
 
       // Precompile constructors to compute information such as
@@ -257,7 +258,7 @@
           set_il_serialization_stream(file);
         }
         if (FLAG_populate_llvm_constant_pool) {
-          auto const object_store = I->object_store();
+          auto const object_store = IG->object_store();
           auto& llvm_constants = GrowableObjectArray::Handle(
               Z, GrowableObjectArray::New(16, Heap::kOld));
           auto& llvm_functions = GrowableObjectArray::Handle(
@@ -292,12 +293,12 @@
 #define DO(member, name)                                                       \
   stub_code = StubCode::BuildIsolateSpecific##name##Stub(                      \
       global_object_pool_builder());                                           \
-  I->object_store()->set_##member(stub_code);
+  IG->object_store()->set_##member(stub_code);
         OBJECT_STORE_STUB_CODE_LIST(DO)
 #undef DO
         stub_code =
             StubCode::GetBuildMethodExtractorStub(global_object_pool_builder());
-        I->object_store()->set_build_method_extractor_code(stub_code);
+        IG->object_store()->set_build_method_extractor_code(stub_code);
       }
 
       CollectDynamicFunctionNames();
@@ -313,15 +314,15 @@
       // not be retained, although they will be used and expected to be
       // canonical.
       AddTypeArguments(
-          TypeArguments::Handle(Z, I->object_store()->type_argument_int()));
+          TypeArguments::Handle(Z, IG->object_store()->type_argument_int()));
       AddTypeArguments(
-          TypeArguments::Handle(Z, I->object_store()->type_argument_double()));
+          TypeArguments::Handle(Z, IG->object_store()->type_argument_double()));
       AddTypeArguments(
-          TypeArguments::Handle(Z, I->object_store()->type_argument_string()));
+          TypeArguments::Handle(Z, IG->object_store()->type_argument_string()));
       AddTypeArguments(TypeArguments::Handle(
-          Z, I->object_store()->type_argument_string_dynamic()));
+          Z, IG->object_store()->type_argument_string_dynamic()));
       AddTypeArguments(TypeArguments::Handle(
-          Z, I->object_store()->type_argument_string_string()));
+          Z, IG->object_store()->type_argument_string_string()));
 
       // Compile newly found targets and add their callees until we reach a
       // fixed point.
@@ -337,7 +338,7 @@
         // dart code stub.
         const auto& pool = ObjectPool::Handle(
             ObjectPool::NewFromBuilder(*global_object_pool_builder()));
-        I->object_store()->set_global_object_pool(pool);
+        IG->object_store()->set_global_object_pool(pool);
         global_object_pool_builder()->Reset();
 
         if (FLAG_print_gop) {
@@ -355,13 +356,13 @@
         if (FLAG_populate_llvm_constant_pool) {
           // We don't want the Array backing for any mappings in the snapshot,
           // only the pools themselves.
-          I->object_store()->set_llvm_constant_hash_table(Array::null_array());
+          IG->object_store()->set_llvm_constant_hash_table(Array::null_array());
 
           // Keep any functions, classes, etc. referenced from the LLVM pools,
           // even if they could have been dropped due to not being otherwise
           // needed at runtime.
           const auto& constant_pool = GrowableObjectArray::Handle(
-              Z, I->object_store()->llvm_constant_pool());
+              Z, IG->object_store()->llvm_constant_pool());
           auto& object = Object::Handle(Z);
           for (intptr_t i = 0; i < constant_pool.Length(); i++) {
             object = constant_pool.At(i);
@@ -376,7 +377,7 @@
           }
 
           const auto& function_pool = GrowableObjectArray::Handle(
-              Z, I->object_store()->llvm_function_pool());
+              Z, IG->object_store()->llvm_function_pool());
           auto& function = Function::Handle(Z);
           for (intptr_t i = 0; i < function_pool.Length(); i++) {
             function ^= function_pool.At(i);
@@ -403,23 +404,23 @@
 
       // Clear these before dropping classes as they may hold onto otherwise
       // dead instances of classes we will remove or otherwise unused symbols.
-      I->object_store()->set_unique_dynamic_targets(Array::null_array());
+      IG->object_store()->set_unique_dynamic_targets(Array::null_array());
       Class& null_class = Class::Handle(Z);
       Function& null_function = Function::Handle(Z);
       Field& null_field = Field::Handle(Z);
-      I->object_store()->set_pragma_class(null_class);
-      I->object_store()->set_pragma_name(null_field);
-      I->object_store()->set_pragma_options(null_field);
-      I->object_store()->set_completer_class(null_class);
-      I->object_store()->set_symbol_class(null_class);
-      I->object_store()->set_compiletime_error_class(null_class);
-      I->object_store()->set_growable_list_factory(null_function);
-      I->object_store()->set_simple_instance_of_function(null_function);
-      I->object_store()->set_simple_instance_of_true_function(null_function);
-      I->object_store()->set_simple_instance_of_false_function(null_function);
-      I->object_store()->set_async_star_move_next_helper(null_function);
-      I->object_store()->set_complete_on_async_return(null_function);
-      I->object_store()->set_async_star_stream_controller(null_class);
+      IG->object_store()->set_pragma_class(null_class);
+      IG->object_store()->set_pragma_name(null_field);
+      IG->object_store()->set_pragma_options(null_field);
+      IG->object_store()->set_completer_class(null_class);
+      IG->object_store()->set_symbol_class(null_class);
+      IG->object_store()->set_compiletime_error_class(null_class);
+      IG->object_store()->set_growable_list_factory(null_function);
+      IG->object_store()->set_simple_instance_of_function(null_function);
+      IG->object_store()->set_simple_instance_of_true_function(null_function);
+      IG->object_store()->set_simple_instance_of_false_function(null_function);
+      IG->object_store()->set_async_star_move_next_helper(null_function);
+      IG->object_store()->set_complete_on_async_return(null_function);
+      IG->object_store()->set_async_star_stream_controller(null_class);
       DropMetadata();
       DropLibraryEntries();
     }
@@ -445,11 +446,11 @@
   intptr_t symbols_after = -1;
   intptr_t capacity = -1;
   if (FLAG_trace_precompiler) {
-    Symbols::GetStats(I, &symbols_before, &capacity);
+    Symbols::GetStats(IG, &symbols_before, &capacity);
   }
 
   if (FLAG_trace_precompiler) {
-    Symbols::GetStats(I, &symbols_after, &capacity);
+    Symbols::GetStats(IG, &symbols_after, &capacity);
     THR_Print("Precompiled %" Pd " functions,", function_count_);
     THR_Print(" %" Pd " dynamic types,", class_count_);
     THR_Print(" %" Pd " dynamic selectors.\n", selector_count_);
@@ -490,7 +491,7 @@
   phase_ = Phase::kCompilingConstructorsForInstructionCounts;
   HANDLESCOPE(T);
   ConstructorVisitor visitor(this, Z);
-  ProgramVisitor::WalkProgram(Z, I, &visitor);
+  ProgramVisitor::WalkProgram(Z, IG, &visitor);
   phase_ = Phase::kPreparation;
 }
 
@@ -502,7 +503,7 @@
 
   AddSelector(Symbols::Call());  // For speed, not correctness.
 
-  const Library& lib = Library::Handle(I->object_store()->root_library());
+  const Library& lib = Library::Handle(IG->object_store()->root_library());
   if (lib.IsNull()) {
     const String& msg = String::Handle(
         Z, String::New("Cannot find root library in isolate.\n"));
@@ -597,7 +598,7 @@
         cids.Clear();
         if (CHA::ConcreteSubclasses(cls, &cids)) {
           for (intptr_t j = 0; j < cids.length(); ++j) {
-            subcls = I->class_table()->At(cids[j]);
+            subcls = IG->class_table()->At(cids[j]);
             if (subcls.is_allocated()) {
               // Add dispatcher to cls.
               dispatcher = subcls.GetInvocationDispatcher(
@@ -947,8 +948,8 @@
 
   class ConstObjectVisitor : public ObjectPointerVisitor {
    public:
-    ConstObjectVisitor(Precompiler* precompiler, Isolate* isolate)
-        : ObjectPointerVisitor(isolate->group()),
+    ConstObjectVisitor(Precompiler* precompiler, IsolateGroup* isolate_group)
+        : ObjectPointerVisitor(isolate_group),
           precompiler_(precompiler),
           subinstance_(Object::Handle()) {}
 
@@ -967,14 +968,14 @@
     Object& subinstance_;
   };
 
-  ConstObjectVisitor visitor(this, I);
+  ConstObjectVisitor visitor(this, IG);
   instance.raw()->ptr()->VisitPointers(&visitor);
 }
 
 void Precompiler::AddClosureCall(const String& call_selector,
                                  const Array& arguments_descriptor) {
   const Class& cache_class =
-      Class::Handle(Z, I->object_store()->closure_class());
+      Class::Handle(Z, IG->object_store()->closure_class());
   const Function& dispatcher =
       Function::Handle(Z, cache_class.GetInvocationDispatcher(
                               call_selector, arguments_descriptor,
@@ -1167,7 +1168,7 @@
       // Check for @pragma on the class itself.
       if (cls.has_pragma()) {
         metadata ^= lib.GetMetadata(cls);
-        if (FindEntryPointPragma(isolate(), metadata, &reusable_field_handle,
+        if (FindEntryPointPragma(IG, metadata, &reusable_field_handle,
                                  &reusable_object_handle) ==
             EntryPointPragma::kAlways) {
           AddInstantiatedClass(cls);
@@ -1184,9 +1185,8 @@
         if (field.has_pragma()) {
           metadata ^= lib.GetMetadata(field);
           if (metadata.IsNull()) continue;
-          EntryPointPragma pragma =
-              FindEntryPointPragma(isolate(), metadata, &reusable_field_handle,
-                                   &reusable_object_handle);
+          EntryPointPragma pragma = FindEntryPointPragma(
+              IG, metadata, &reusable_field_handle, &reusable_object_handle);
           if (pragma == EntryPointPragma::kNever) continue;
 
           AddField(field);
@@ -1211,9 +1211,8 @@
         if (function.has_pragma()) {
           metadata ^= lib.GetMetadata(function);
           if (metadata.IsNull()) continue;
-          auto type =
-              FindEntryPointPragma(isolate(), metadata, &reusable_field_handle,
-                                   &reusable_object_handle);
+          auto type = FindEntryPointPragma(IG, metadata, &reusable_field_handle,
+                                           &reusable_object_handle);
 
           if (type == EntryPointPragma::kAlways ||
               type == EntryPointPragma::kCallOnly) {
@@ -1538,8 +1537,7 @@
               functions_map.NumOccupied(), table.NumOccupied());
   }
 
-  isolate()->object_store()->set_unique_dynamic_targets(
-      functions_map.Release());
+  IG->object_store()->set_unique_dynamic_targets(functions_map.Release());
   table.Release();
 }
 
@@ -1590,7 +1588,7 @@
     }
   }
 
-  closures = isolate()->object_store()->closure_functions();
+  closures = IG->object_store()->closure_functions();
   for (intptr_t j = 0; j < closures.Length(); j++) {
     function ^= closures.At(j);
     bool retain = possibly_retained_functions_.ContainsKey(function);
@@ -1618,7 +1616,7 @@
   // dropping functions, as we may clear references to Code objects.
   const auto& entries =
       Array::Handle(Z, dispatch_table_generator_->BuildCodeArray());
-  I->object_store()->set_dispatch_table_code_entries(entries);
+  IG->object_store()->set_dispatch_table_code_entries(entries);
   // Delete the dispatch table generator to ensure there's no attempt
   // to add new entries after this point.
   delete dispatch_table_generator_;
@@ -1696,7 +1694,7 @@
 
   HANDLESCOPE(T);
   StaticCallTableEntryFixer visitor(Z);
-  ProgramVisitor::WalkProgram(Z, I, &visitor);
+  ProgramVisitor::WalkProgram(Z, IG, &visitor);
 }
 
 void Precompiler::DropFunctions() {
@@ -1785,7 +1783,7 @@
     }
   }
 
-  closures = isolate()->object_store()->closure_functions();
+  closures = IG->object_store()->closure_functions();
   retained_functions = GrowableObjectArray::New();
   for (intptr_t j = 0; j < closures.Length(); j++) {
     function ^= closures.At(j);
@@ -1795,7 +1793,7 @@
       drop_function(function);
     }
   }
-  isolate()->object_store()->set_closure_functions(retained_functions);
+  IG->object_store()->set_closure_functions(retained_functions);
 }
 
 void Precompiler::DropFields() {
@@ -1852,7 +1850,7 @@
 }
 
 void Precompiler::AttachOptimizedTypeTestingStub() {
-  Isolate::Current()->heap()->CollectAllGarbage();
+  IsolateGroup::Current()->heap()->CollectAllGarbage();
   GrowableHandlePtrArray<const AbstractType> types(Z, 200);
   {
     class TypesCollector : public ObjectVisitor {
@@ -1877,10 +1875,10 @@
     TypesCollector visitor(Z, &types);
 
     // Find all type objects in this isolate.
-    I->heap()->VisitObjects(&visitor);
+    IG->heap()->VisitObjects(&visitor);
 
     // Find all type objects in the vm-isolate.
-    Dart::vm_isolate()->heap()->VisitObjects(&visitor);
+    Dart::vm_isolate_group()->heap()->VisitObjects(&visitor);
   }
 
   TypeUsageInfo* type_usage_info = Thread::Current()->type_usage_info();
@@ -1915,7 +1913,7 @@
 }
 
 void Precompiler::DropTypes() {
-  ObjectStore* object_store = I->object_store();
+  ObjectStore* object_store = IG->object_store();
   GrowableObjectArray& retained_types =
       GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
   Array& types_array = Array::Handle(Z);
@@ -1952,7 +1950,7 @@
 }
 
 void Precompiler::DropTypeParameters() {
-  ObjectStore* object_store = I->object_store();
+  ObjectStore* object_store = IG->object_store();
   GrowableObjectArray& retained_typeparams =
       GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
   Array& typeparams_array = Array::Handle(Z);
@@ -1994,7 +1992,7 @@
 }
 
 void Precompiler::DropTypeArguments() {
-  ObjectStore* object_store = I->object_store();
+  ObjectStore* object_store = IG->object_store();
   Array& typeargs_array = Array::Handle(Z);
   GrowableObjectArray& retained_typeargs =
       GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
@@ -2192,7 +2190,7 @@
 
     lib.RehashDictionary(dict, used * 4 / 3 + 1);
     if (!(retain_root_library_caches_ &&
-          (lib.raw() == I->object_store()->root_library()))) {
+          (lib.raw() == IG->object_store()->root_library()))) {
       lib.DropDependenciesAndCaches();
     }
   }
@@ -2207,10 +2205,10 @@
   // corpses because the class table entry may be used to find the size of
   // corpses. Request a full GC and wait for the sweeper tasks to finish before
   // we continue.
-  I->heap()->CollectAllGarbage();
-  I->heap()->WaitForSweeperTasks(T);
+  IG->heap()->CollectAllGarbage();
+  IG->heap()->WaitForSweeperTasks(T);
 
-  ClassTable* class_table = I->class_table();
+  ClassTable* class_table = IG->class_table();
   intptr_t num_cids = class_table->NumCids();
 
   for (intptr_t cid = kNumPredefinedCids; cid < num_cids; cid++) {
@@ -2250,7 +2248,7 @@
   const GrowableObjectArray& retained_libraries =
       GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
   const Library& root_lib =
-      Library::Handle(Z, I->object_store()->root_library());
+      Library::Handle(Z, IG->object_store()->root_library());
   Library& lib = Library::Handle(Z);
   Class& toplevel_class = Class::Handle(Z);
 
@@ -2288,7 +2286,7 @@
     } else {
       toplevel_class = lib.toplevel_class();
 
-      I->class_table()->UnregisterTopLevel(toplevel_class.id());
+      IG->class_table()->UnregisterTopLevel(toplevel_class.id());
       toplevel_class.set_id(kIllegalCid);  // We check this when serializing.
 
       dropped_library_count_++;
@@ -2332,7 +2330,7 @@
   };
 
   CodeChecker visitor;
-  ProgramVisitor::WalkProgram(Z, I, &visitor);
+  ProgramVisitor::WalkProgram(Z, IG, &visitor);
   const CodeSet& visited = visitor.visited();
 
   FunctionSet::Iterator it(&functions_to_retain_);
@@ -2372,11 +2370,11 @@
   };
 
   GrowableHandlePtrArray<const Script> scripts(Z, 100);
-  Isolate::Current()->heap()->CollectAllGarbage();
+  IsolateGroup::Current()->heap()->CollectAllGarbage();
   {
     HeapIterationScope his(T);
     ScriptsCollector visitor(Z, &scripts);
-    I->heap()->VisitObjects(&visitor);
+    IG->heap()->VisitObjects(&visitor);
   }
 
   {
@@ -2416,7 +2414,7 @@
   IG->set_obfuscation_map(Obfuscator::SerializeMap(T));
 
   // Discard obfuscation mappings to avoid including them into snapshot.
-  I->object_store()->set_obfuscation_map(Array::Handle(Z));
+  IG->object_store()->set_obfuscation_map(Array::Handle(Z));
 }
 
 void Precompiler::FinalizeAllClasses() {
@@ -2978,7 +2976,7 @@
 void Obfuscator::ObfuscationState::SaveState() {
   saved_state_.SetAt(kSavedStateNameIndex, String::Handle(String::New(name_)));
   saved_state_.SetAt(kSavedStateRenamesIndex, renames_.Release());
-  thread_->isolate()->object_store()->set_obfuscation_map(saved_state_);
+  thread_->isolate_group()->object_store()->set_obfuscation_map(saved_state_);
 }
 
 void Obfuscator::ObfuscationState::PreventRenaming(const char* name) {
@@ -3084,8 +3082,9 @@
 
 void Obfuscator::Deobfuscate(Thread* thread,
                              const GrowableObjectArray& pieces) {
-  const Array& obfuscation_state = Array::Handle(
-      thread->zone(), thread->isolate()->object_store()->obfuscation_map());
+  const Array& obfuscation_state =
+      Array::Handle(thread->zone(),
+                    thread->isolate_group()->object_store()->obfuscation_map());
   if (obfuscation_state.IsNull()) {
     return;
   }
@@ -3134,8 +3133,9 @@
 }
 
 const char** Obfuscator::SerializeMap(Thread* thread) {
-  const Array& obfuscation_state = Array::Handle(
-      thread->zone(), thread->isolate()->object_store()->obfuscation_map());
+  const Array& obfuscation_state =
+      Array::Handle(thread->zone(),
+                    thread->isolate_group()->object_store()->obfuscation_map());
   if (obfuscation_state.IsNull()) {
     return NULL;
   }
diff --git a/runtime/vm/compiler/assembler/assembler_ia32_test.cc b/runtime/vm/compiler/assembler/assembler_ia32_test.cc
index 6c13141..4f98613 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32_test.cc
+++ b/runtime/vm/compiler/assembler/assembler_ia32_test.cc
@@ -4588,7 +4588,7 @@
 }
 
 ASSEMBLER_TEST_GENERATE(TestObjectCompare, assembler) {
-  ObjectStore* object_store = Isolate::Current()->object_store();
+  ObjectStore* object_store = IsolateGroup::Current()->object_store();
   const Object& obj = Object::ZoneHandle(object_store->smi_class());
   Label fail;
   __ LoadObject(EAX, obj);
diff --git a/runtime/vm/compiler/assembler/assembler_x64_test.cc b/runtime/vm/compiler/assembler/assembler_x64_test.cc
index 7a21b97..39d492d 100644
--- a/runtime/vm/compiler/assembler/assembler_x64_test.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64_test.cc
@@ -4935,7 +4935,7 @@
 }
 
 ASSEMBLER_TEST_GENERATE(TestObjectCompare, assembler) {
-  ObjectStore* object_store = Isolate::Current()->object_store();
+  ObjectStore* object_store = IsolateGroup::Current()->object_store();
   const Object& obj = Object::ZoneHandle(object_store->smi_class());
   Label fail;
   EnterTestFrame(assembler);
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 7e10988..683544b 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -551,7 +551,7 @@
   // that is actually valid on a null receiver.
   if (receiver_maybe_null) {
     const Class& null_class =
-        Class::Handle(zone(), isolate()->object_store()->null_class());
+        Class::Handle(zone(), isolate_group()->object_store()->null_class());
     Function& target = Function::Handle(zone());
     if (null_class.EnsureIsFinalized(thread()) == Error::null()) {
       target = Resolver::ResolveDynamicAnyArgs(zone(), null_class, method_name);
@@ -607,8 +607,8 @@
 
 void FlowGraph::AddExactnessGuard(InstanceCallInstr* call,
                                   intptr_t receiver_cid) {
-  const Class& cls = Class::Handle(
-      zone(), Isolate::Current()->class_table()->At(receiver_cid));
+  const Class& cls =
+      Class::Handle(zone(), isolate_group()->class_table()->At(receiver_cid));
 
   Definition* load_type_args = new (zone()) LoadFieldInstr(
       call->Receiver()->CopyWithType(),
diff --git a/runtime/vm/compiler/backend/flow_graph.h b/runtime/vm/compiler/backend/flow_graph.h
index 6f0c5b9..1e60eea 100644
--- a/runtime/vm/compiler/backend/flow_graph.h
+++ b/runtime/vm/compiler/backend/flow_graph.h
@@ -239,6 +239,7 @@
   Thread* thread() const { return thread_; }
   Zone* zone() const { return thread()->zone(); }
   Isolate* isolate() const { return thread()->isolate(); }
+  IsolateGroup* isolate_group() const { return thread()->isolate_group(); }
 
   intptr_t max_block_id() const { return max_block_id_; }
   void set_max_block_id(intptr_t id) { max_block_id_ = id; }
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index be04219..4f7810b9 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -160,14 +160,15 @@
       intrinsic_mode_(false),
       stats_(stats),
       double_class_(
-          Class::ZoneHandle(isolate()->object_store()->double_class())),
-      mint_class_(Class::ZoneHandle(isolate()->object_store()->mint_class())),
-      float32x4_class_(
-          Class::ZoneHandle(isolate()->object_store()->float32x4_class())),
-      float64x2_class_(
-          Class::ZoneHandle(isolate()->object_store()->float64x2_class())),
+          Class::ZoneHandle(isolate_group()->object_store()->double_class())),
+      mint_class_(
+          Class::ZoneHandle(isolate_group()->object_store()->mint_class())),
+      float32x4_class_(Class::ZoneHandle(
+          isolate_group()->object_store()->float32x4_class())),
+      float64x2_class_(Class::ZoneHandle(
+          isolate_group()->object_store()->float64x2_class())),
       int32x4_class_(
-          Class::ZoneHandle(isolate()->object_store()->int32x4_class())),
+          Class::ZoneHandle(isolate_group()->object_store()->int32x4_class())),
       list_class_(Class::ZoneHandle(Library::Handle(Library::CoreLibrary())
                                         .LookupClass(Symbols::List()))),
       parallel_move_resolver_(this),
@@ -285,7 +286,8 @@
 }
 
 bool FlowGraphCompiler::CanOSRFunction() const {
-  return isolate()->use_osr() && CanOptimizeFunction() && !is_optimizing();
+  return isolate_group()->use_osr() && CanOptimizeFunction() &&
+         !is_optimizing();
 }
 
 void FlowGraphCompiler::InsertBSSRelocation(BSS::Relocation reloc) {
@@ -300,7 +302,7 @@
   if ((FLAG_stacktrace_every > 0) || (FLAG_deoptimize_every > 0) ||
       (FLAG_gc_every > 0) ||
       (isolate()->reload_every_n_stack_overflow_checks() > 0)) {
-    if (!Isolate::IsSystemIsolate(isolate())) {
+    if (!IsolateGroup::IsSystemIsolateGroup(isolate_group())) {
       return true;
     }
   }
@@ -2118,13 +2120,13 @@
                                         const ArgumentsDescriptor& args_desc,
                                         Function* fn_return,
                                         bool* class_is_abstract_return) {
-  Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
-  Zone* zone = thread->zone();
+  auto thread = Thread::Current();
+  auto zone = thread->zone();
+  auto class_table = thread->isolate_group()->class_table();
   if (class_id < 0) return false;
-  if (class_id >= isolate->class_table()->NumCids()) return false;
+  if (class_id >= class_table->NumCids()) return false;
 
-  ClassPtr raw_class = isolate->class_table()->At(class_id);
+  ClassPtr raw_class = class_table->At(class_id);
   if (raw_class == nullptr) return false;
   Class& cls = Class::Handle(zone, raw_class);
   if (cls.IsNull()) return false;
@@ -2703,8 +2705,8 @@
     __ BranchIf(EQUAL, is_instance_lbl);
     __ CompareObject(
         kScratchReg,
-        Type::ZoneHandle(zone(),
-                         isolate()->object_store()->nullable_object_type()));
+        Type::ZoneHandle(
+            zone(), isolate_group()->object_store()->nullable_object_type()));
     __ BranchIf(EQUAL, is_instance_lbl);
     __ CompareObject(kScratchReg, Object::void_type());
     __ BranchIf(EQUAL, is_instance_lbl);
@@ -3216,7 +3218,7 @@
 CodePtr NullErrorSlowPath::GetStub(FlowGraphCompiler* compiler,
                                    CheckNullInstr::ExceptionType exception_type,
                                    bool save_fpu_registers) {
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   switch (exception_type) {
     case CheckNullInstr::kNoSuchMethod:
       return save_fpu_registers
@@ -3271,7 +3273,7 @@
   const Field& original_field = Field::ZoneHandle(
       instruction()->AsLoadField()->slot().field().Original());
   __ LoadObject(LateInitializationErrorABI::kFieldReg, original_field);
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& stub = Code::ZoneHandle(
       compiler->zone(),
       save_fpu_registers
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
index 543ad37..3d45acb 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
@@ -31,7 +31,7 @@
 
 void FlowGraphCompiler::ArchSpecificInitialization() {
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    auto object_store = isolate()->object_store();
+    auto object_store = isolate_group()->object_store();
 
     const auto& stub =
         Code::ZoneHandle(object_store->write_barrier_wrappers_stub());
@@ -258,7 +258,7 @@
   ASSERT(extracted_method.IsZoneHandle());
 
   const Code& build_method_extractor = Code::ZoneHandle(
-      isolate()->object_store()->build_method_extractor_code());
+      isolate_group()->object_store()->build_method_extractor_code());
 
   const intptr_t stub_index = __ object_pool_builder().AddObject(
       build_method_extractor, ObjectPool::Patchability::kNotPatchable);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
index 88f8e0b..33d1808 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
@@ -30,7 +30,7 @@
 
 void FlowGraphCompiler::ArchSpecificInitialization() {
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    auto object_store = isolate()->object_store();
+    auto object_store = isolate_group()->object_store();
 
     const auto& stub =
         Code::ZoneHandle(object_store->write_barrier_wrappers_stub());
@@ -249,7 +249,7 @@
   ASSERT(extracted_method.IsZoneHandle());
 
   const Code& build_method_extractor = Code::ZoneHandle(
-      isolate()->object_store()->build_method_extractor_code());
+      isolate_group()->object_store()->build_method_extractor_code());
 
   const intptr_t stub_index = __ object_pool_builder().AddObject(
       build_method_extractor, ObjectPool::Patchability::kNotPatchable);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
index 5cd4559..25444d0 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
@@ -30,7 +30,7 @@
 
 void FlowGraphCompiler::ArchSpecificInitialization() {
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    auto object_store = isolate()->object_store();
+    auto object_store = isolate_group()->object_store();
 
     const auto& stub =
         Code::ZoneHandle(object_store->write_barrier_wrappers_stub());
@@ -258,7 +258,7 @@
   ASSERT(extracted_method.IsZoneHandle());
 
   const Code& build_method_extractor = Code::ZoneHandle(
-      isolate()->object_store()->build_method_extractor_code());
+      isolate_group()->object_store()->build_method_extractor_code());
   ASSERT(!build_method_extractor.IsNull());
 
   const intptr_t stub_index = __ object_pool_builder().AddObject(
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 938488f..cbf6c5c 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -115,7 +115,7 @@
     const Class& klass,
     bool include_abstract,
     bool exclude_null) {
-  ClassTable* table = thread()->isolate()->class_table();
+  ClassTable* table = thread()->isolate_group()->class_table();
   const intptr_t cid_count = table->NumCids();
   std::unique_ptr<CidRangeVector[]>* cid_ranges = nullptr;
   if (include_abstract) {
@@ -143,7 +143,7 @@
 
 const CidRangeVector& HierarchyInfo::SubclassRangesForClass(
     const Class& klass) {
-  ClassTable* table = thread()->isolate()->class_table();
+  ClassTable* table = thread()->isolate_group()->class_table();
   const intptr_t cid_count = table->NumCids();
   if (cid_subclass_ranges_ == nullptr) {
     cid_subclass_ranges_.reset(new CidRangeVector[cid_count]);
@@ -176,7 +176,7 @@
                                    bool include_abstract,
                                    bool exclude_null) {
   Zone* zone = thread()->zone();
-  ClassTable* class_table = thread()->isolate()->class_table();
+  ClassTable* class_table = thread()->isolate_group()->class_table();
 
   // Only really used if `use_subtype_test == true`.
   const Type& dst_type = Type::Handle(zone, Type::RawCast(klass.RareType()));
@@ -3962,7 +3962,7 @@
 
   // Spread class-ids to following classes where a lookup yields the same
   // method.
-  const intptr_t max_cid = Isolate::Current()->class_table()->NumCids();
+  const intptr_t max_cid = IsolateGroup::Current()->class_table()->NumCids();
   for (int idx = 0; idx < length; idx++) {
     int upper_limit_cid =
         (idx == length - 1) ? max_cid : targets[idx + 1].cid_start;
@@ -4335,7 +4335,7 @@
     __ LoadObject(InitStaticFieldABI::kFieldReg,
                   Field::ZoneHandle(field().Original()));
 
-    auto object_store = compiler->isolate()->object_store();
+    auto object_store = compiler->isolate_group()->object_store();
     const auto& init_static_field_stub = Code::ZoneHandle(
         compiler->zone(), object_store->init_static_field_stub());
     compiler->GenerateStubCall(source(), init_static_field_stub,
@@ -4372,7 +4372,7 @@
 
   __ LoadObject(InitInstanceFieldABI::kFieldReg, original_field);
 
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   auto& stub = Code::ZoneHandle(compiler->zone());
   if (field.needs_load_guard()) {
     stub = object_store->init_instance_field_stub();
@@ -4414,7 +4414,7 @@
 }
 
 void ThrowInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& throw_stub =
       Code::ZoneHandle(compiler->zone(), object_store->throw_stub());
 
@@ -4441,7 +4441,7 @@
 }
 
 void ReThrowInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& re_throw_stub =
       Code::ZoneHandle(compiler->zone(), object_store->re_throw_stub());
 
@@ -4473,7 +4473,7 @@
   // Check that the type of the value is allowed in conditional context.
   ASSERT(locs()->always_calls());
 
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& assert_boolean_stub =
       Code::ZoneHandle(compiler->zone(), object_store->assert_boolean_stub());
 
@@ -5503,7 +5503,7 @@
 #if defined(TARGET_ARCH_IA32)
   UNREACHABLE();
 #else
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& stub = Code::ZoneHandle(
       compiler->zone(),
       save_fpu_registers
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index c4d4140..5ff3ed1 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -3151,8 +3151,8 @@
 void CreateArrayInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
   TypeUsageInfo* type_usage_info = compiler->thread()->type_usage_info();
   if (type_usage_info != nullptr) {
-    const Class& list_class = Class::Handle(
-        compiler->thread()->isolate()->class_table()->At(kArrayCid));
+    const Class& list_class =
+        Class::Handle(compiler->isolate_group()->class_table()->At(kArrayCid));
     RegisterTypeArgumentsUse(compiler->function(), type_usage_info, list_class,
                              element_type()->definition());
   }
@@ -3176,7 +3176,7 @@
   }
 
   __ Bind(&slow_path);
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& allocate_array_stub =
       Code::ZoneHandle(compiler->zone(), object_store->allocate_array_stub());
   compiler->GenerateStubCall(source(), allocate_array_stub,
@@ -3574,7 +3574,7 @@
 
     compiler->SaveLiveRegisters(locs);
 
-    auto object_store = compiler->isolate()->object_store();
+    auto object_store = compiler->isolate_group()->object_store();
     const auto& allocate_context_stub = Code::ZoneHandle(
         compiler->zone(), object_store->allocate_context_stub());
     __ LoadImmediate(R1, instruction()->num_context_variables());
@@ -3624,7 +3624,7 @@
   ASSERT(locs()->temp(0).reg() == R1);
   ASSERT(locs()->out(0).reg() == R0);
 
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& allocate_context_stub =
       Code::ZoneHandle(compiler->zone(), object_store->allocate_context_stub());
   __ LoadImmediate(R1, num_context_variables());
@@ -3647,7 +3647,7 @@
   ASSERT(locs()->in(0).reg() == R4);
   ASSERT(locs()->out(0).reg() == R0);
 
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& clone_context_stub =
       Code::ZoneHandle(compiler->zone(), object_store->clone_context_stub());
   compiler->GenerateStubCall(source(), clone_context_stub,
@@ -3726,7 +3726,7 @@
       : TemplateSlowPathCode(instruction) {}
 
   virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
-    if (compiler->isolate()->use_osr() && osr_entry_label()->IsLinked()) {
+    if (compiler->isolate_group()->use_osr() && osr_entry_label()->IsLinked()) {
       const Register value = instruction()->locs()->temp(0).reg();
       __ Comment("CheckStackOverflowSlowPathOsr");
       __ Bind(osr_entry_label());
@@ -3766,7 +3766,7 @@
           kStackOverflowRuntimeEntry, kNumSlowPathArgs, instruction()->locs());
     }
 
-    if (compiler->isolate()->use_osr() && !compiler->is_optimizing() &&
+    if (compiler->isolate_group()->use_osr() && !compiler->is_optimizing() &&
         instruction()->in_loop()) {
       // In unoptimized code, record loop stack checks as possible OSR entries.
       compiler->AddCurrentDescriptor(PcDescriptorsLayout::kOsrEntry,
@@ -3781,7 +3781,7 @@
   }
 
   compiler::Label* osr_entry_label() {
-    ASSERT(Isolate::Current()->use_osr());
+    ASSERT(IsolateGroup::Current()->use_osr());
     return &osr_entry_label_;
   }
 
@@ -3794,7 +3794,7 @@
                                compiler::target::Thread::stack_limit_offset()));
   __ cmp(SP, compiler::Operand(IP));
 
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const bool live_fpu_regs = locs()->live_registers()->FpuRegisterCount() > 0;
   const auto& stub = Code::ZoneHandle(
       compiler->zone(),
@@ -5007,7 +5007,7 @@
   // Shared slow path is used in BoxInt64Instr::EmitNativeCode in
   // FLAG_use_bare_instructions mode and only after VM isolate stubs where
   // replaced with isolate-specific stubs.
-  auto object_store = Isolate::Current()->object_store();
+  auto object_store = IsolateGroup::Current()->object_store();
   const bool stubs_in_vm_isolate =
       object_store->allocate_mint_with_fpu_regs_stub()
           ->ptr()
@@ -5064,7 +5064,7 @@
     __ TryAllocate(compiler->mint_class(),
                    compiler->intrinsic_slow_path_label(), out_reg, tmp);
   } else if (locs()->call_on_shared_slow_path()) {
-    auto object_store = compiler->isolate()->object_store();
+    auto object_store = compiler->isolate_group()->object_store();
     const bool live_fpu_regs = locs()->live_registers()->FpuRegisterCount() > 0;
     const auto& stub = Code::ZoneHandle(
         compiler->zone(),
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index a3fe07a..7c54051 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -2699,8 +2699,8 @@
 void CreateArrayInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
   TypeUsageInfo* type_usage_info = compiler->thread()->type_usage_info();
   if (type_usage_info != nullptr) {
-    const Class& list_class = Class::Handle(
-        compiler->thread()->isolate()->class_table()->At(kArrayCid));
+    const Class& list_class =
+        Class::Handle(compiler->isolate_group()->class_table()->At(kArrayCid));
     RegisterTypeArgumentsUse(compiler->function(), type_usage_info, list_class,
                              element_type()->definition());
   }
@@ -2723,7 +2723,7 @@
   }
 
   __ Bind(&slow_path);
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& allocate_array_stub =
       Code::ZoneHandle(compiler->zone(), object_store->allocate_array_stub());
   compiler->GenerateStubCall(source(), allocate_array_stub,
@@ -3087,7 +3087,7 @@
 
     compiler->SaveLiveRegisters(locs);
 
-    auto object_store = compiler->isolate()->object_store();
+    auto object_store = compiler->isolate_group()->object_store();
     const auto& allocate_context_stub = Code::ZoneHandle(
         compiler->zone(), object_store->allocate_context_stub());
 
@@ -3138,7 +3138,7 @@
   ASSERT(locs()->temp(0).reg() == R1);
   ASSERT(locs()->out(0).reg() == R0);
 
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& allocate_context_stub =
       Code::ZoneHandle(compiler->zone(), object_store->allocate_context_stub());
   __ LoadImmediate(R1, num_context_variables());
@@ -3161,7 +3161,7 @@
   ASSERT(locs()->in(0).reg() == R5);
   ASSERT(locs()->out(0).reg() == R0);
 
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& clone_context_stub =
       Code::ZoneHandle(compiler->zone(), object_store->clone_context_stub());
   compiler->GenerateStubCall(source(), clone_context_stub,
@@ -3240,7 +3240,7 @@
 
   virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
     auto locs = instruction()->locs();
-    if (compiler->isolate()->use_osr() && osr_entry_label()->IsLinked()) {
+    if (compiler->isolate_group()->use_osr() && osr_entry_label()->IsLinked()) {
       const Register value = locs->temp(0).reg();
       __ Comment("CheckStackOverflowSlowPathOsr");
       __ Bind(osr_entry_label());
@@ -3262,7 +3262,7 @@
     compiler->pending_deoptimization_env_ = env;
 
     if (using_shared_stub) {
-      auto object_store = compiler->isolate()->object_store();
+      auto object_store = compiler->isolate_group()->object_store();
       const bool live_fpu_regs = locs->live_registers()->FpuRegisterCount() > 0;
       const auto& stub = Code::ZoneHandle(
           compiler->zone(),
@@ -3291,7 +3291,7 @@
           kStackOverflowRuntimeEntry, kNumSlowPathArgs, locs);
     }
 
-    if (compiler->isolate()->use_osr() && !compiler->is_optimizing() &&
+    if (compiler->isolate_group()->use_osr() && !compiler->is_optimizing() &&
         instruction()->in_loop()) {
       // In unoptimized code, record loop stack checks as possible OSR entries.
       compiler->AddCurrentDescriptor(PcDescriptorsLayout::kOsrEntry,
@@ -3306,7 +3306,7 @@
   }
 
   compiler::Label* osr_entry_label() {
-    ASSERT(Isolate::Current()->use_osr());
+    ASSERT(IsolateGroup::Current()->use_osr());
     return &osr_entry_label_;
   }
 
@@ -4233,7 +4233,7 @@
   // Shared slow path is used in BoxInt64Instr::EmitNativeCode in
   // FLAG_use_bare_instructions mode and only after VM isolate stubs where
   // replaced with isolate-specific stubs.
-  auto object_store = Isolate::Current()->object_store();
+  auto object_store = IsolateGroup::Current()->object_store();
   const bool stubs_in_vm_isolate =
       object_store->allocate_mint_with_fpu_regs_stub()
           ->ptr()
@@ -4283,7 +4283,7 @@
     __ TryAllocate(compiler->mint_class(),
                    compiler->intrinsic_slow_path_label(), out, temp);
   } else if (locs()->call_on_shared_slow_path()) {
-    auto object_store = compiler->isolate()->object_store();
+    auto object_store = compiler->isolate_group()->object_store();
     const bool live_fpu_regs = locs()->live_registers()->FpuRegisterCount() > 0;
     const auto& stub = Code::ZoneHandle(
         compiler->zone(),
diff --git a/runtime/vm/compiler/backend/il_deserializer.cc b/runtime/vm/compiler/backend/il_deserializer.cc
index 2dcf6a6..a122337 100644
--- a/runtime/vm/compiler/backend/il_deserializer.cc
+++ b/runtime/vm/compiler/backend/il_deserializer.cc
@@ -1572,7 +1572,7 @@
   auto const ref_sexp = Retrieve(list, 1);
   if (ref_sexp == nullptr) return false;
   if (auto const cid_sexp = ref_sexp->AsInteger()) {
-    ClassTable* table = thread()->isolate()->class_table();
+    ClassTable* table = thread()->isolate_group()->class_table();
     if (!table->HasValidClassAt(cid_sexp->value())) {
       StoreError(cid_sexp, "no valid class found for cid");
       return false;
@@ -1688,7 +1688,7 @@
   auto const cid_sexp = CheckInteger(Retrieve(list, 1));
   if (cid_sexp == nullptr) return false;
 
-  auto const table = thread()->isolate()->class_table();
+  auto const table = thread()->isolate_group()->class_table();
   if (!table->HasValidClassAt(cid_sexp->value())) {
     StoreError(cid_sexp, "cid is not valid");
     return false;
@@ -1941,7 +1941,7 @@
   } else if (auto const class_sexp =
                  CheckInteger(list->ExtraLookupValue("class"))) {
     const intptr_t cid = class_sexp->value();
-    auto const table = thread()->isolate()->class_table();
+    auto const table = thread()->isolate_group()->class_table();
     if (!table->HasValidClassAt(cid)) {
       StoreError(class_sexp, "not a valid class id");
       return false;
@@ -2333,7 +2333,7 @@
     ic_data.set_is_megamorphic(is_mega_sexp->value());
   }
 
-  auto const class_table = thread()->isolate()->class_table();
+  auto const class_table = thread()->isolate_group()->class_table();
   GrowableArray<intptr_t> class_ids(zone(), 2);
   for (intptr_t i = 1, n = list->Length(); i < n; i++) {
     auto const entry = CheckList(Retrieve(list, i));
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index fa15876..9f68daa 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -2530,7 +2530,7 @@
   }
 
   __ Bind(&slow_path);
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& allocate_array_stub =
       Code::ZoneHandle(compiler->zone(), object_store->allocate_array_stub());
   compiler->GenerateStubCall(source(), allocate_array_stub,
@@ -3025,7 +3025,7 @@
       : TemplateSlowPathCode(instruction) {}
 
   virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
-    if (compiler->isolate()->use_osr() && osr_entry_label()->IsLinked()) {
+    if (compiler->isolate_group()->use_osr() && osr_entry_label()->IsLinked()) {
       __ Comment("CheckStackOverflowSlowPathOsr");
       __ Bind(osr_entry_label());
       __ movl(compiler::Address(THR, Thread::stack_overflow_flags_offset()),
@@ -3044,7 +3044,7 @@
         instruction()->source(), instruction()->deopt_id(),
         kStackOverflowRuntimeEntry, 0, instruction()->locs());
 
-    if (compiler->isolate()->use_osr() && !compiler->is_optimizing() &&
+    if (compiler->isolate_group()->use_osr() && !compiler->is_optimizing() &&
         instruction()->in_loop()) {
       // In unoptimized code, record loop stack checks as possible OSR entries.
       compiler->AddCurrentDescriptor(PcDescriptorsLayout::kOsrEntry,
@@ -3057,7 +3057,7 @@
   }
 
   compiler::Label* osr_entry_label() {
-    ASSERT(Isolate::Current()->use_osr());
+    ASSERT(IsolateGroup::Current()->use_osr());
     return &osr_entry_label_;
   }
 
diff --git a/runtime/vm/compiler/backend/il_printer.cc b/runtime/vm/compiler/backend/il_printer.cc
index 530f4ce..e971b18 100644
--- a/runtime/vm/compiler/backend/il_printer.cc
+++ b/runtime/vm/compiler/backend/il_printer.cc
@@ -132,8 +132,8 @@
       f->AddString(" | ");
     }
     if (range.IsSingleCid()) {
-      const Class& cls =
-          Class::Handle(Isolate::Current()->class_table()->At(range.cid_start));
+      const Class& cls = Class::Handle(
+          IsolateGroup::Current()->class_table()->At(range.cid_start));
       f->Printf("%s", String::Handle(cls.Name()).ToCString());
       f->Printf(" cid %" Pd " cnt:%" Pd " trgt:'%s'", range.cid_start, count,
                 target.ToQualifiedCString());
@@ -168,8 +168,8 @@
     if (i > 0) {
       f->AddString(" | ");
     }
-    const Class& cls =
-        Class::Handle(Isolate::Current()->class_table()->At(range.cid_start));
+    const Class& cls = Class::Handle(
+        IsolateGroup::Current()->class_table()->At(range.cid_start));
     f->Printf("%s etc. ", String::Handle(cls.Name()).ToCString());
     if (range.IsSingleCid()) {
       f->Printf(" cid %" Pd, range.cid_start);
@@ -209,8 +209,8 @@
       if (k > 0) {
         f->AddString(", ");
       }
-      const Class& cls =
-          Class::Handle(Isolate::Current()->class_table()->At(class_ids[k]));
+      const Class& cls = Class::Handle(
+          IsolateGroup::Current()->class_table()->At(class_ids[k]));
       f->Printf("%s", String::Handle(cls.Name()).ToCString());
     }
     f->Printf(" cnt:%" Pd " trgt:'%s'", count, target.ToQualifiedCString());
@@ -233,7 +233,7 @@
     const intptr_t count = ic_data.GetCountAt(i);
     const intptr_t cid = ic_data.GetReceiverClassIdAt(i);
     const Class& cls =
-        Class::Handle(Isolate::Current()->class_table()->At(cid));
+        Class::Handle(IsolateGroup::Current()->class_table()->At(cid));
     f->Printf("%s : %" Pd ", ", String::Handle(cls.Name()).ToCString(), count);
   }
   f->AddString("]");
@@ -814,14 +814,14 @@
 void CheckClassIdInstr::PrintOperandsTo(BaseTextBuffer* f) const {
   value()->PrintTo(f);
 
-  const Class& cls =
-      Class::Handle(Isolate::Current()->class_table()->At(cids().cid_start));
+  const Class& cls = Class::Handle(
+      IsolateGroup::Current()->class_table()->At(cids().cid_start));
   const String& name = String::Handle(cls.ScrubbedName());
   if (cids().IsSingleCid()) {
     f->Printf(", %s", name.ToCString());
   } else {
-    const Class& cls2 =
-        Class::Handle(Isolate::Current()->class_table()->At(cids().cid_end));
+    const Class& cls2 = Class::Handle(
+        IsolateGroup::Current()->class_table()->At(cids().cid_end));
     const String& name2 = String::Handle(cls2.ScrubbedName());
     f->Printf(", cid %" Pd "-%" Pd " %s-%s", cids().cid_start, cids().cid_end,
               name.ToCString(), name2.ToCString());
diff --git a/runtime/vm/compiler/backend/il_serializer.cc b/runtime/vm/compiler/backend/il_serializer.cc
index eeff8fb..863e8ab 100644
--- a/runtime/vm/compiler/backend/il_serializer.cc
+++ b/runtime/vm/compiler/backend/il_serializer.cc
@@ -37,7 +37,7 @@
                                          const FlowGraph* flow_graph)
     : flow_graph_(ASSERT_NOTNULL(flow_graph)),
       zone_(zone),
-      object_store_(flow_graph->thread()->isolate()->object_store()),
+      object_store_(flow_graph->thread()->isolate_group()->object_store()),
       open_recursive_types_(zone_),
       llvm_constants_(
           GrowableObjectArray::Handle(zone_,
diff --git a/runtime/vm/compiler/backend/il_test_helper.h b/runtime/vm/compiler/backend/il_test_helper.h
index ddacf5f..8f9b5f8 100644
--- a/runtime/vm/compiler/backend/il_test_helper.h
+++ b/runtime/vm/compiler/backend/il_test_helper.h
@@ -321,7 +321,7 @@
         /*is_abstract=*/false,
         /*is_external=*/false,
         /*is_native=*/true,
-        Class::Handle(thread->isolate()->object_store()->object_class()),
+        Class::Handle(thread->isolate_group()->object_store()->object_class()),
         TokenPosition::kNoSource));
 
     Zone* zone = thread->zone();
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 80ddf22..b49f95c 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -2719,8 +2719,8 @@
 void CreateArrayInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
   TypeUsageInfo* type_usage_info = compiler->thread()->type_usage_info();
   if (type_usage_info != nullptr) {
-    const Class& list_class = Class::Handle(
-        compiler->thread()->isolate()->class_table()->At(kArrayCid));
+    const Class& list_class =
+        Class::Handle(compiler->isolate_group()->class_table()->At(kArrayCid));
     RegisterTypeArgumentsUse(compiler->function(), type_usage_info, list_class,
                              element_type()->definition());
   }
@@ -2743,7 +2743,7 @@
   }
 
   __ Bind(&slow_path);
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& allocate_array_stub =
       Code::ZoneHandle(compiler->zone(), object_store->allocate_array_stub());
   compiler->GenerateStubCall(source(), allocate_array_stub,
@@ -3115,7 +3115,7 @@
 
     compiler->SaveLiveRegisters(locs);
 
-    auto object_store = compiler->isolate()->object_store();
+    auto object_store = compiler->isolate_group()->object_store();
     const auto& allocate_context_stub = Code::ZoneHandle(
         compiler->zone(), object_store->allocate_context_stub());
 
@@ -3167,7 +3167,7 @@
   ASSERT(locs()->temp(0).reg() == R10);
   ASSERT(locs()->out(0).reg() == RAX);
 
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& allocate_context_stub =
       Code::ZoneHandle(compiler->zone(), object_store->allocate_context_stub());
 
@@ -3191,7 +3191,7 @@
   ASSERT(locs()->in(0).reg() == R9);
   ASSERT(locs()->out(0).reg() == RAX);
 
-  auto object_store = compiler->isolate()->object_store();
+  auto object_store = compiler->isolate_group()->object_store();
   const auto& clone_context_stub =
       Code::ZoneHandle(compiler->zone(), object_store->clone_context_stub());
   compiler->GenerateStubCall(source(), clone_context_stub,
@@ -3271,7 +3271,7 @@
       : TemplateSlowPathCode(instruction) {}
 
   virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
-    if (compiler->isolate()->use_osr() && osr_entry_label()->IsLinked()) {
+    if (compiler->isolate_group()->use_osr() && osr_entry_label()->IsLinked()) {
       __ Comment("CheckStackOverflowSlowPathOsr");
       __ Bind(osr_entry_label());
       __ movq(compiler::Address(THR, Thread::stack_overflow_flags_offset()),
@@ -3308,7 +3308,7 @@
           kStackOverflowRuntimeEntry, kNumSlowPathArgs, instruction()->locs());
     }
 
-    if (compiler->isolate()->use_osr() && !compiler->is_optimizing() &&
+    if (compiler->isolate_group()->use_osr() && !compiler->is_optimizing() &&
         instruction()->in_loop()) {
       // In unoptimized code, record loop stack checks as possible OSR entries.
       compiler->AddCurrentDescriptor(PcDescriptorsLayout::kOsrEntry,
@@ -3323,7 +3323,7 @@
   }
 
   compiler::Label* osr_entry_label() {
-    ASSERT(Isolate::Current()->use_osr());
+    ASSERT(IsolateGroup::Current()->use_osr());
     return &osr_entry_label_;
   }
 
@@ -4504,7 +4504,7 @@
   // Shared slow path is used in BoxInt64Instr::EmitNativeCode in
   // FLAG_use_bare_instructions mode and only after VM isolate stubs where
   // replaced with isolate-specific stubs.
-  auto object_store = Isolate::Current()->object_store();
+  auto object_store = IsolateGroup::Current()->object_store();
   const bool stubs_in_vm_isolate =
       object_store->allocate_mint_with_fpu_regs_stub()
           ->ptr()
@@ -4554,7 +4554,7 @@
                    compiler->intrinsic_slow_path_label(),
                    compiler::Assembler::kNearJump, out, temp);
   } else if (locs()->call_on_shared_slow_path()) {
-    auto object_store = compiler->isolate()->object_store();
+    auto object_store = compiler->isolate_group()->object_store();
     const bool live_fpu_regs = locs()->live_registers()->FpuRegisterCount() > 0;
     const auto& stub = Code::ZoneHandle(
         compiler->zone(),
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index bbc0700..1fb023c 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -4130,7 +4130,7 @@
         type = Type::IntType();
       } else if (receiver_cid != kClosureCid) {
         const Class& cls = Class::Handle(
-            Z, flow_graph->isolate()->class_table()->At(receiver_cid));
+            Z, flow_graph->isolate_group()->class_table()->At(receiver_cid));
         if (!cls.IsGeneric()) {
           type = cls.DeclarationType();
         }
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index 5661445..f623a31 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -1684,7 +1684,6 @@
 
   ~LoadOptimizer() { aliased_set_->RollbackAliasedIdentites(); }
 
-  Isolate* isolate() const { return graph_->isolate(); }
   Zone* zone() const { return graph_->zone(); }
 
   static bool OptimizeGraph(FlowGraph* graph) {
@@ -3571,11 +3570,11 @@
     num_elements = instr->num_context_variables();
   } else if (auto instr = alloc->AsCreateArray()) {
     cls = &Class::ZoneHandle(
-        flow_graph_->isolate()->object_store()->array_class());
+        flow_graph_->isolate_group()->object_store()->array_class());
     num_elements = instr->GetConstantNumElements();
   } else if (auto instr = alloc->AsAllocateTypedData()) {
     cls = &Class::ZoneHandle(
-        flow_graph_->isolate()->class_table()->At(instr->class_id()));
+        flow_graph_->isolate_group()->class_table()->At(instr->class_id()));
     num_elements = instr->GetConstantNumElements();
   } else {
     UNREACHABLE();
@@ -3691,11 +3690,13 @@
     }
   }
   if (alloc->IsCreateArray()) {
-    AddSlot(slots,
-            Slot::GetTypeArgumentsSlotFor(
-                flow_graph_->thread(),
-                Class::Handle(
-                    Z, flow_graph_->isolate()->object_store()->array_class())));
+    AddSlot(
+        slots,
+        Slot::GetTypeArgumentsSlotFor(
+            flow_graph_->thread(),
+            Class::Handle(
+                Z,
+                flow_graph_->isolate_group()->object_store()->array_class())));
   }
 
   // Collect all instructions that mention this object in the environment.
diff --git a/runtime/vm/compiler/backend/slot.cc b/runtime/vm/compiler/backend/slot.cc
index f0a4f2c..bf23f54 100644
--- a/runtime/vm/compiler/backend/slot.cc
+++ b/runtime/vm/compiler/backend/slot.cc
@@ -319,7 +319,7 @@
   // itself stays behind in the compilation global cache. Thus we must always
   // try to add it to the list of guarded fields of the current function.
   if (slot.is_guarded_field()) {
-    if (thread->isolate()->use_field_guards()) {
+    if (thread->isolate_group()->use_field_guards()) {
       ASSERT(parsed_function != nullptr);
       parsed_function->AddToGuardedFields(&slot.field());
     } else {
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index e841d39..4b25afc 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -303,7 +303,7 @@
   }
   Thread* thread = Thread::Current();
   const Class& null_class =
-      Class::Handle(thread->isolate()->object_store()->null_class());
+      Class::Handle(thread->isolate_group()->object_store()->null_class());
   Function& target = Function::Handle();
   if (Error::Handle(null_class.EnsureIsFinalized(thread)).IsNull()) {
     target = Resolver::ResolveDynamicAnyArgs(thread->zone(), null_class,
@@ -775,8 +775,8 @@
       return type_;
     }
 
-    Isolate* I = Isolate::Current();
-    const Class& type_class = Class::Handle(I->class_table()->At(cid_));
+    auto IG = IsolateGroup::Current();
+    const Class& type_class = Class::Handle(IG->class_table()->At(cid_));
     type_ = &AbstractType::ZoneHandle(type_class.RareType());
   }
 
@@ -887,7 +887,7 @@
     return;
   } else if ((cid_ != kIllegalCid) && (cid_ != kDynamicCid)) {
     const Class& cls =
-        Class::Handle(Isolate::Current()->class_table()->At(cid_));
+        Class::Handle(IsolateGroup::Current()->class_table()->At(cid_));
     type_name = String::Handle(cls.ScrubbedName()).ToCString();
   } else if (type_ != NULL) {
     type_name = type_->IsDynamicType()
@@ -1356,7 +1356,7 @@
             ? TypeArguments::null_type_arguments()
             : TypeArguments::Cast(type_args_value->BoundConstant());
     const Class& cls =
-        Class::Handle(Isolate::Current()->class_table()->At(cid));
+        Class::Handle(IsolateGroup::Current()->class_table()->At(cid));
     Type& type = Type::ZoneHandle(Type::New(
         cls, type_args, TokenPosition::kNoSource, Nullability::kNonNullable));
     ASSERT(type.IsInstantiated());
@@ -1753,7 +1753,7 @@
   if (cid == kGrowableObjectArrayCid || cid == kArrayCid ||
       cid == kImmutableArrayCid ||
       array_type.type_class() ==
-          Isolate::Current()->object_store()->list_class()) {
+          IsolateGroup::Current()->object_store()->list_class()) {
     const auto& type_args = TypeArguments::Handle(array_type.arguments());
     return type_args.TypeAtNullSafe(Array::kElementTypeTypeArgPos);
   }
diff --git a/runtime/vm/compiler/backend/type_propagator_test.cc b/runtime/vm/compiler/backend/type_propagator_test.cc
index 5c30a19..026fa6c 100644
--- a/runtime/vm/compiler/backend/type_propagator_test.cc
+++ b/runtime/vm/compiler/backend/type_propagator_test.cc
@@ -167,7 +167,7 @@
   CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
 
   const Class& object_class =
-      Class::Handle(thread->isolate()->object_store()->object_class());
+      Class::Handle(thread->isolate_group()->object_store()->object_class());
 
   const Function& target_func = Function::ZoneHandle(Function::New(
       String::Handle(Symbols::New(thread, "dummy2")),
diff --git a/runtime/vm/compiler/backend/typed_data_aot_test.cc b/runtime/vm/compiler/backend/typed_data_aot_test.cc
index 93a2a79..8e7ddd1 100644
--- a/runtime/vm/compiler/backend/typed_data_aot_test.cc
+++ b/runtime/vm/compiler/backend/typed_data_aot_test.cc
@@ -473,7 +473,7 @@
   const auto& test_function =
       Function::Handle(GetFunction(root_library, "test"));
   const auto& closures = GrowableObjectArray::Handle(
-      Isolate::Current()->object_store()->closure_functions());
+      IsolateGroup::Current()->object_store()->closure_functions());
   auto& function = Function::Handle();
   for (intptr_t i = closures.Length() - 1; 0 <= i; ++i) {
     function ^= closures.At(i);
diff --git a/runtime/vm/compiler/backend/yield_position_test.cc b/runtime/vm/compiler/backend/yield_position_test.cc
index e90be85..e541f67 100644
--- a/runtime/vm/compiler/backend/yield_position_test.cc
+++ b/runtime/vm/compiler/backend/yield_position_test.cc
@@ -79,7 +79,7 @@
 
   // Grab the inner, lazily created, closure from the object store.
   const auto& closures = GrowableObjectArray::Handle(
-      Isolate::Current()->object_store()->closure_functions());
+      IsolateGroup::Current()->object_store()->closure_functions());
   ASSERT(!closures.IsNull());
   auto& closure = Object::Handle();
   for (intptr_t i = 0; i < closures.Length(); ++i) {
diff --git a/runtime/vm/compiler/call_specializer.cc b/runtime/vm/compiler/call_specializer.cc
index 53014cc..7d815bf 100644
--- a/runtime/vm/compiler/call_specializer.cc
+++ b/runtime/vm/compiler/call_specializer.cc
@@ -13,7 +13,7 @@
 namespace dart {
 
 // Quick access to the current isolate and zone.
-#define I (isolate())
+#define IG (isolate_group())
 #define Z (zone())
 
 static void RefineUseTypes(Definition* instr) {
@@ -150,7 +150,7 @@
 
   if (all_cids_known) {
     const Class& receiver_class =
-        Class::Handle(Z, isolate()->class_table()->At(class_ids[0]));
+        Class::Handle(Z, IG->class_table()->At(class_ids[0]));
     if (!receiver_class.is_finalized()) {
       // Do not eagerly finalize classes. ResolveDynamicForReceiverClass can
       // cause class finalization, since callee's receiver class may not be
@@ -839,7 +839,7 @@
     }
   }
 
-  if (I->use_field_guards()) {
+  if (IG->use_field_guards()) {
     if (field.guarded_cid() != kDynamicCid) {
       InsertBefore(instr,
                    new (Z)
@@ -1096,7 +1096,7 @@
     }
   }
 
-  const ClassTable& class_table = *isolate()->class_table();
+  const ClassTable& class_table = *IG->class_table();
   Bool& prev = Bool::Handle(Z);
   Class& cls = Class::Handle(Z);
 
@@ -1454,7 +1454,7 @@
     ZoneGrowableArray<intptr_t>* results,
     const AbstractType& type) {
   ASSERT(results->length() >= 2);  // At least on entry.
-  const ClassTable& class_table = *Isolate::Current()->class_table();
+  const ClassTable& class_table = *IsolateGroup::Current()->class_table();
   if ((*results)[0] != kSmiCid) {
     const Class& smi_class = Class::Handle(class_table.At(kSmiCid));
     const bool smi_is_subtype =
diff --git a/runtime/vm/compiler/call_specializer.h b/runtime/vm/compiler/call_specializer.h
index 5bb2cb7..ccad4cb 100644
--- a/runtime/vm/compiler/call_specializer.h
+++ b/runtime/vm/compiler/call_specializer.h
@@ -75,6 +75,7 @@
  protected:
   Thread* thread() const { return flow_graph_->thread(); }
   Isolate* isolate() const { return flow_graph_->isolate(); }
+  IsolateGroup* isolate_group() const { return flow_graph_->isolate_group(); }
   Zone* zone() const { return flow_graph_->zone(); }
   const Function& function() const { return flow_graph_->function(); }
 
diff --git a/runtime/vm/compiler/cha.cc b/runtime/vm/compiler/cha.cc
index eed831e..6e0f9d3 100644
--- a/runtime/vm/compiler/cha.cc
+++ b/runtime/vm/compiler/cha.cc
@@ -51,7 +51,7 @@
 }
 
 bool CHA::HasSubclasses(intptr_t cid) const {
-  const ClassTable& class_table = *thread_->isolate()->class_table();
+  const ClassTable& class_table = *thread_->isolate_group()->class_table();
   Class& cls = Class::Handle(thread_->zone(), class_table.At(cid));
   return HasSubclasses(cls);
 }
diff --git a/runtime/vm/compiler/cha_test.cc b/runtime/vm/compiler/cha_test.cc
index c2c605c..b4ea6ce 100644
--- a/runtime/vm/compiler/cha_test.cc
+++ b/runtime/vm/compiler/cha_test.cc
@@ -99,7 +99,7 @@
   EXPECT(cha.IsGuardedClass(class_d.id()));
 
   const Class& closure_class =
-      Class::Handle(Isolate::Current()->object_store()->closure_class());
+      Class::Handle(IsolateGroup::Current()->object_store()->closure_class());
   EXPECT(!cha.HasSubclasses(closure_class.id()));
 }
 
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
index ea5dfb0..3e47d9d 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
@@ -17,7 +17,7 @@
 namespace kernel {
 
 #define Z (zone_)
-#define I (thread_->isolate())
+#define IG (thread_->isolate_group())
 
 Fragment& Fragment::operator+=(const Fragment& other) {
   if (entry == NULL) {
@@ -546,7 +546,7 @@
         kind /* = StoreInstanceFieldInstr::Kind::kOther */) {
   Fragment instructions;
   const Field& field_clone = MayCloneField(Z, field);
-  if (I->use_field_guards()) {
+  if (IG->use_field_guards()) {
     LocalVariable* store_expression = MakeTemporary();
     instructions += LoadLocal(store_expression);
     instructions += GuardFieldClass(field_clone, GetNextDeoptId());
@@ -874,7 +874,7 @@
 
   Fragment failing(nsm);
   const Code& nsm_handler = Code::ZoneHandle(
-      Z, I->object_store()->call_closure_no_such_method_stub());
+      Z, IG->object_store()->call_closure_no_such_method_stub());
   failing += LoadArgDescriptor();
   failing += TailCall(nsm_handler);
 
@@ -906,7 +906,7 @@
 Fragment BaseFlowGraphBuilder::AllocateClosure(
     TokenPosition position,
     const Function& closure_function) {
-  const Class& cls = Class::ZoneHandle(Z, I->object_store()->closure_class());
+  const Class& cls = Class::ZoneHandle(Z, IG->object_store()->closure_class());
   AllocateObjectInstr* allocate =
       new (Z) AllocateObjectInstr(InstructionSource(position), cls);
   allocate->set_closure_function(closure_function);
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index ad07173..def0672 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -19,6 +19,7 @@
 #define H (translation_helper_)
 #define T (type_translator_)
 #define I Isolate::Current()
+#define IG IsolateGroup::Current()
 #define B (flow_graph_builder_)
 
 Class& StreamingFlowGraphBuilder::GetSuperOrDie() {
@@ -708,7 +709,7 @@
   Fragment body;
   if ((dart_function.NumParameters() == 2) &&
       (dart_function.name() == Symbols::EqualOperator().raw()) &&
-      (dart_function.Owner() != I->object_store()->object_class())) {
+      (dart_function.Owner() != IG->object_store()->object_class())) {
     TargetEntryInstr* null_entry;
     TargetEntryInstr* non_null_entry;
 
@@ -3579,7 +3580,7 @@
     instructions += IntConstant(0);
     instructions += StaticCall(
         position,
-        Function::ZoneHandle(Z, I->object_store()->growable_list_factory()), 2,
+        Function::ZoneHandle(Z, IG->object_store()->growable_list_factory()), 2,
         ICData::kStatic);
     return instructions;
   }
@@ -3756,7 +3757,7 @@
 Fragment StreamingFlowGraphBuilder::BuildFutureNullValue(
     TokenPosition* position) {
   if (position != NULL) *position = TokenPosition::kNoSource;
-  const Class& future = Class::Handle(Z, I->object_store()->future_class());
+  const Class& future = Class::Handle(Z, IG->object_store()->future_class());
   ASSERT(!future.IsNull());
   const auto& error = future.EnsureIsFinalized(thread());
   ASSERT(error == Error::null());
@@ -3797,7 +3798,7 @@
 
   instructions += AllocateObject(
       TokenPosition::kNoSource,
-      Class::ZoneHandle(Z, I->object_store()->closure_class()), 0);
+      Class::ZoneHandle(Z, IG->object_store()->closure_class()), 0);
   LocalVariable* new_closure = MakeTemporary();
 
   intptr_t num_type_args = ReadListLength();
@@ -3920,7 +3921,7 @@
 }
 
 Fragment StreamingFlowGraphBuilder::BuildAssertBlock() {
-  if (!I->asserts()) {
+  if (!IG->asserts()) {
     SkipStatementList();
     return Fragment();
   }
@@ -3944,7 +3945,7 @@
 }
 
 Fragment StreamingFlowGraphBuilder::BuildAssertStatement() {
-  if (!I->asserts()) {
+  if (!IG->asserts()) {
     SetOffset(ReaderOffset() - 1);  // Include the tag.
     SkipStatement();                // read this statement.
     return Fragment();
@@ -5111,10 +5112,10 @@
   code += Constant(result);
 
   auto& ffi_callback_functions = GrowableObjectArray::Handle(Z);
-  ffi_callback_functions ^= I->object_store()->ffi_callback_functions();
+  ffi_callback_functions ^= IG->object_store()->ffi_callback_functions();
   if (ffi_callback_functions.IsNull()) {
     ffi_callback_functions ^= GrowableObjectArray::New();
-    I->object_store()->set_ffi_callback_functions(ffi_callback_functions);
+    IG->object_store()->set_ffi_callback_functions(ffi_callback_functions);
   }
   ffi_callback_functions.Add(result);
 
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index f248794..5983ca2 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -1324,7 +1324,7 @@
       } else {
         body += Box(native_rep.AsRepresentationOverApprox(zone_));
         if (kind == MethodRecognizer::kFfiLoadPointer) {
-          const auto class_table = thread_->isolate()->class_table();
+          const auto class_table = thread_->isolate_group()->class_table();
           ASSERT(class_table->HasValidClassAt(kFfiPointerCid));
           const auto& pointer_class =
               Class::ZoneHandle(H.zone(), class_table->At(kFfiPointerCid));
@@ -1384,7 +1384,7 @@
 
       if (kind == MethodRecognizer::kFfiStorePointer) {
         // Do type check before anything untagged is on the stack.
-        const auto class_table = thread_->isolate()->class_table();
+        const auto class_table = thread_->isolate_group()->class_table();
         ASSERT(class_table->HasValidClassAt(kFfiPointerCid));
         const auto& pointer_class =
             Class::ZoneHandle(H.zone(), class_table->At(kFfiPointerCid));
@@ -1454,7 +1454,7 @@
       body += NullConstant();
     } break;
     case MethodRecognizer::kFfiFromAddress: {
-      const auto class_table = thread_->isolate()->class_table();
+      const auto class_table = thread_->isolate_group()->class_table();
       ASSERT(class_table->HasValidClassAt(kFfiPointerCid));
       const auto& pointer_class =
           Class::ZoneHandle(H.zone(), class_table->At(kFfiPointerCid));
@@ -1497,7 +1497,7 @@
     const Function& function,
     classid_t cid) {
   auto token_pos = function.token_pos();
-  auto class_table = Thread::Current()->isolate()->class_table();
+  auto class_table = Thread::Current()->isolate_group()->class_table();
 
   ASSERT(class_table->HasValidClassAt(cid));
   const auto& view_class = Class::ZoneHandle(H.zone(), class_table->At(cid));
@@ -1550,7 +1550,8 @@
     const Function& function,
     classid_t cid) {
   const auto token_pos = function.token_pos();
-  ASSERT(Thread::Current()->isolate()->class_table()->HasValidClassAt(cid));
+  ASSERT(
+      Thread::Current()->isolate_group()->class_table()->HasValidClassAt(cid));
 
   ASSERT(function.IsFactory() && (function.NumParameters() == 2));
   LocalVariable* length = parsed_function_->RawParameterVariable(1);
@@ -2056,7 +2057,7 @@
     // If noSuchMethod is not found on the receiver class, call
     // Object.noSuchMethod.
     no_such_method = Resolver::ResolveDynamicForReceiverClass(
-        Class::Handle(Z, I->object_store()->object_class()),
+        Class::Handle(Z, IG->object_store()->object_class()),
         Symbols::NoSuchMethod(), two_arguments);
   }
   body += StaticCall(TokenPosition::kMinSource, no_such_method,
@@ -2811,7 +2812,7 @@
 
   // Determine if this is `class Closure { get call => this; }`
   const Class& closure_class =
-      Class::Handle(Z, I->object_store()->closure_class());
+      Class::Handle(Z, IG->object_store()->closure_class());
   const bool is_closure_call = (owner.raw() == closure_class.raw()) &&
                                field_name.Equals(Symbols::Call());
 
@@ -3695,7 +3696,7 @@
 }
 
 Fragment FlowGraphBuilder::UnhandledException() {
-  const auto class_table = thread_->isolate()->class_table();
+  const auto class_table = thread_->isolate_group()->class_table();
   ASSERT(class_table->HasValidClassAt(kUnhandledExceptionCid));
   const auto& klass =
       Class::ZoneHandle(H.zone(), class_table->At(kUnhandledExceptionCid));
@@ -4209,7 +4210,7 @@
   body += LoadNativeField(Slot::Closure_context());
   body += LoadNativeField(Slot::GetContextVariableSlotFor(
       thread_, *MakeImplicitClosureScope(
-                    Z, Class::Handle(I->object_store()->ffi_pointer_class()))
+                    Z, Class::Handle(IG->object_store()->ffi_pointer_class()))
                     ->context_variables()[0]));
 
   // This can only be Pointer, so it is always safe to LoadUntagged.
@@ -4464,7 +4465,7 @@
 
 Fragment FlowGraphBuilder::BuildNullAssertions() {
   Fragment code;
-  if (IG->null_safety() || !I->asserts() || !FLAG_null_assertions) {
+  if (IG->null_safety() || !IG->asserts() || !FLAG_null_assertions) {
     return code;
   }
 
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index 748a25d..f18153d 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -1856,7 +1856,7 @@
     loading_units.SetAt(id, unit);
   }
 
-  ObjectStore* object_store = Isolate::Current()->object_store();
+  ObjectStore* object_store = IG->object_store();
   ASSERT(object_store->loading_units() == Array::null());
   object_store->set_loading_units(loading_units);
 }
@@ -2965,16 +2965,16 @@
     case kNeverType: {
       const Nullability nullability = helper_->ReadNullability();
       if (apply_legacy_erasure_) {
-        result_ = I->object_store()->null_type();
+        result_ = IG->object_store()->null_type();
       } else {
-        result_ = Type::Handle(Z, I->object_store()->never_type())
+        result_ = Type::Handle(Z, IG->object_store()->never_type())
                       .ToNullability(nullability, Heap::kOld);
       }
       break;
     }
     case kBottomType:
       // Map Bottom type to Null type until not emitted by CFE anymore.
-      result_ = I->object_store()->null_type();
+      result_ = IG->object_store()->null_type();
       ASSERT(result_.IsNullable());
       break;
     case kInterfaceType:
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index ba75d00..4c6f1af 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -14,6 +14,7 @@
 #define H (translation_helper_)
 #define T (type_translator_)
 #define I Isolate::Current()
+#define IG IsolateGroup::Current()
 
 ScopeBuilder::ScopeBuilder(ParsedFunction* parsed_function)
     : result_(NULL),
@@ -964,7 +965,7 @@
     case kEmptyStatement:
       return;
     case kAssertBlock:
-      if (I->asserts()) {
+      if (IG->asserts()) {
         PositionScope scope(&helper_.reader_);
         intptr_t offset =
             helper_.ReaderOffset() - 1;  // -1 to include tag byte.
@@ -984,7 +985,7 @@
       }
       return;
     case kAssertStatement:
-      if (I->asserts()) {
+      if (IG->asserts()) {
         VisitExpression();            // Read condition.
         helper_.ReadPosition();       // read condition start offset.
         helper_.ReadPosition();       // read condition end offset.
diff --git a/runtime/vm/compiler/relocation.cc b/runtime/vm/compiler/relocation.cc
index a14f615..89305aa 100644
--- a/runtime/vm/compiler/relocation.cc
+++ b/runtime/vm/compiler/relocation.cc
@@ -433,7 +433,7 @@
     // into the types directly, but that does not work for types which
     // live in the "vm-isolate" - such as `Type::dynamic_type()`).
     if (destination_.InVMIsolateHeap()) {
-      auto object_store = thread_->isolate()->object_store();
+      auto object_store = thread_->isolate_group()->object_store();
 
       if (destination_.raw() == StubCode::DefaultTypeTest().raw()) {
         destination_ = object_store->default_tts_stub();
diff --git a/runtime/vm/compiler/runtime_api.cc b/runtime/vm/compiler/runtime_api.cc
index 739f168..e2965c9 100644
--- a/runtime/vm/compiler/runtime_api.cc
+++ b/runtime/vm/compiler/runtime_api.cc
@@ -161,17 +161,17 @@
 }
 
 const Class& GrowableObjectArrayClass() {
-  auto object_store = Isolate::Current()->object_store();
+  auto object_store = IsolateGroup::Current()->object_store();
   return Class::Handle(object_store->growable_object_array_class());
 }
 
 const Class& MintClass() {
-  auto object_store = Isolate::Current()->object_store();
+  auto object_store = IsolateGroup::Current()->object_store();
   return Class::Handle(object_store->mint_class());
 }
 
 const Class& DoubleClass() {
-  auto object_store = Isolate::Current()->object_store();
+  auto object_store = IsolateGroup::Current()->object_store();
   return Class::Handle(object_store->double_class());
 }
 
@@ -430,7 +430,7 @@
 }
 
 bool Class::TraceAllocation(const dart::Class& klass) {
-  return klass.TraceAllocation(dart::Isolate::Current());
+  return klass.TraceAllocation(dart::IsolateGroup::Current());
 }
 
 word Instance::first_field_offset() {
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index 9fb6e93..0f635af 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -675,7 +675,7 @@
 void StubCodeCompiler::GenerateAllocateUnhandledExceptionStub(
     Assembler* assembler) {
   Thread* thread = Thread::Current();
-  auto class_table = thread->isolate()->class_table();
+  auto class_table = thread->isolate_group()->class_table();
   ASSERT(class_table->HasValidClassAt(kUnhandledExceptionCid));
   const auto& cls = Class::ZoneHandle(thread->zone(),
                                       class_table->At(kUnhandledExceptionCid));
diff --git a/runtime/vm/compiler_test.cc b/runtime/vm/compiler_test.cc
index 1a5afc1..9dcd004 100644
--- a/runtime/vm/compiler_test.cc
+++ b/runtime/vm/compiler_test.cc
@@ -267,8 +267,9 @@
   EXPECT(val.IsInteger());
   EXPECT_EQ(7, Integer::Cast(val).AsInt64Value());
 
-  intptr_t initial_class_table_size =
-      Isolate::Current()->class_table()->NumCids();
+  auto class_table = IsolateGroup::Current()->class_table();
+
+  intptr_t initial_class_table_size = class_table->NumCids();
 
   val = Api::UnwrapHandle(
       TestCase::EvaluateExpression(lib, expression,
@@ -279,8 +280,7 @@
   EXPECT(val.IsInteger());
   EXPECT_EQ(7, Integer::Cast(val).AsInt64Value());
 
-  intptr_t final_class_table_size =
-      Isolate::Current()->class_table()->NumCids();
+  intptr_t final_class_table_size = class_table->NumCids();
   // Eval should not eat into this non-renewable resource.
   EXPECT_EQ(initial_class_table_size, final_class_table_size);
 }
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index a4bede9..3eab3b1 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -294,13 +294,13 @@
     ASSERT(T != NULL);
     StackZone zone(T);
     HandleScope handle_scope(T);
-    Object::InitNullAndBool(vm_isolate_);
+    Object::InitNullAndBool(vm_isolate_->group());
     vm_isolate_->set_object_store(new ObjectStore());
     vm_isolate_->isolate_object_store()->Init();
     vm_isolate_->isolate_group_->object_store_ =
         vm_isolate_->object_store_shared_ptr_;
     TargetCPUFeatures::Init();
-    Object::Init(vm_isolate_);
+    Object::Init(vm_isolate_->group());
     ArgumentsDescriptor::Init();
     ICData::Init();
     SubtypeTestCache::Init();
@@ -326,7 +326,7 @@
             "Precompiled runtime requires a precompiled snapshot");
 #else
         StubCode::Init();
-        Object::FinishInit(vm_isolate_);
+        Object::FinishInit(vm_isolate_->group());
         // MallocHooks can't be initialized until StubCode has been since stack
         // trace generation relies on stub methods that are generated in
         // StubCode::Init().
@@ -345,24 +345,24 @@
         return Utils::StrDup(error.ToErrorCString());
       }
 
-      Object::FinishInit(vm_isolate_);
+      Object::FinishInit(vm_isolate_->group());
 #if defined(SUPPORT_TIMELINE)
       if (tbes.enabled()) {
         tbes.SetNumArguments(2);
         tbes.FormatArgument(0, "snapshotSize", "%" Pd, snapshot->length());
         tbes.FormatArgument(
             1, "heapSize", "%" Pd64,
-            vm_isolate_->heap()->UsedInWords(Heap::kOld) * kWordSize);
+            vm_isolate_group()->heap()->UsedInWords(Heap::kOld) * kWordSize);
       }
 #endif  // !defined(PRODUCT)
       if (FLAG_trace_isolates) {
         OS::PrintErr("Size of vm isolate snapshot = %" Pd "\n",
                      snapshot->length());
-        vm_isolate_->heap()->PrintSizes();
+        vm_isolate_group()->heap()->PrintSizes();
         MegamorphicCacheTable::PrintSizes(vm_isolate_);
         intptr_t size;
         intptr_t capacity;
-        Symbols::GetStats(vm_isolate_, &size, &capacity);
+        Symbols::GetStats(vm_isolate_->group(), &size, &capacity);
         OS::PrintErr("VM Isolate: Number of symbols : %" Pd "\n", size);
         OS::PrintErr("VM Isolate: Symbol table capacity : %" Pd "\n", capacity);
       }
@@ -373,7 +373,7 @@
 #else
       vm_snapshot_kind_ = Snapshot::kNone;
       StubCode::Init();
-      Object::FinishInit(vm_isolate_);
+      Object::FinishInit(vm_isolate_->group());
       // MallocHooks can't be initialized until StubCode has been since stack
       // trace generation relies on stub methods that are generated in
       // StubCode::Init().
@@ -381,7 +381,7 @@
       // initialization for the actual malloc hooks to increase accuracy of
       // memory consumption statistics.
       MallocHooks::Init();
-      Symbols::Init(vm_isolate_);
+      Symbols::Init(vm_isolate_->group());
 #endif
     }
     // We need to initialize the constants here for the vm isolate thread due to
@@ -397,10 +397,10 @@
 #if defined(SUPPORT_TIMELINE)
       TimelineBeginEndScope tbes(Timeline::GetVMStream(), "FinalizeVMIsolate");
 #endif
-      Object::FinalizeVMIsolate(vm_isolate_);
+      Object::FinalizeVMIsolate(vm_isolate_->group());
     }
 #if defined(DEBUG)
-    vm_isolate_->heap()->Verify(kRequireMarked);
+    vm_isolate_group()->heap()->Verify(kRequireMarked);
 #endif
   }
   // Allocate the "persistent" scoped handles for the predefined API
@@ -693,13 +693,14 @@
                                        const uint8_t* snapshot_instructions,
                                        const uint8_t* kernel_buffer,
                                        intptr_t kernel_buffer_size) {
+  auto IG = I->group();
   if (kernel_buffer != nullptr) {
-    SafepointReadRwLocker reader(T, I->group()->program_lock());
+    SafepointReadRwLocker reader(T, IG->program_lock());
     I->field_table()->MarkReadyToUse();
   }
 
   Error& error = Error::Handle(T->zone());
-  error = Object::Init(I, kernel_buffer, kernel_buffer_size);
+  error = Object::Init(IG, kernel_buffer, kernel_buffer_size);
   if (!error.IsNull()) {
     return error.raw();
   }
@@ -732,8 +733,8 @@
     }
 
     {
-      SafepointReadRwLocker reader(T, I->group()->program_lock());
-      I->set_field_table(T, I->group()->initial_field_table()->Clone(I));
+      SafepointReadRwLocker reader(T, IG->program_lock());
+      I->set_field_table(T, IG->initial_field_table()->Clone(I));
       I->field_table()->MarkReadyToUse();
     }
 
@@ -742,11 +743,11 @@
       tbes.SetNumArguments(2);
       tbes.FormatArgument(0, "snapshotSize", "%" Pd, snapshot->length());
       tbes.FormatArgument(1, "heapSize", "%" Pd64,
-                          I->heap()->UsedInWords(Heap::kOld) * kWordSize);
+                          IG->heap()->UsedInWords(Heap::kOld) * kWordSize);
     }
 #endif  // defined(SUPPORT_TIMELINE)
     if (FLAG_trace_isolates) {
-      I->heap()->PrintSizes();
+      IG->heap()->PrintSizes();
       MegamorphicCacheTable::PrintSizes(I);
     }
   } else {
@@ -816,8 +817,8 @@
   StackZone printing_zone(T);
   HandleScope printing_scope(T);
   TextBuffer b(1000);
-  const auto& constants =
-      GrowableObjectArray::Handle(I->object_store()->llvm_constant_pool());
+  const auto& constants = GrowableObjectArray::Handle(
+      I->group()->object_store()->llvm_constant_pool());
   if (constants.IsNull()) {
     b.AddString("No constant pool information in snapshot.\n\n");
   } else {
@@ -838,8 +839,8 @@
     }
     b.AddString("End of constant pool.\n\n");
   }
-  const auto& functions =
-      GrowableObjectArray::Handle(I->object_store()->llvm_function_pool());
+  const auto& functions = GrowableObjectArray::Handle(
+      I->group()->object_store()->llvm_function_pool());
   if (functions.IsNull()) {
     b.AddString("No function pool information in snapshot.\n\n");
   } else {
@@ -866,6 +867,7 @@
   // Initialize the new isolate.
   Thread* T = Thread::Current();
   Isolate* I = T->isolate();
+  auto IG = T->isolate_group();
 #if defined(SUPPORT_TIMELINE)
   TimelineBeginEndScope tbes(T, Timeline::GetIsolateStream(),
                              "InitializeIsolate");
@@ -903,7 +905,7 @@
   }
 
   Object::VerifyBuiltinVtables();
-  DEBUG_ONLY(I->heap()->Verify(kForbidMarked));
+  DEBUG_ONLY(IG->heap()->Verify(kForbidMarked));
 
 #if defined(DART_PRECOMPILED_RUNTIME)
   const bool kIsAotRuntime = true;
@@ -913,7 +915,7 @@
 
   if (kIsAotRuntime || was_child_cloned_into_existing_isolate) {
 #if !defined(TARGET_ARCH_IA32)
-    ASSERT(I->object_store()->build_method_extractor_code() != Code::null());
+    ASSERT(IG->object_store()->build_method_extractor_code() != Code::null());
 #endif
 #if defined(DART_PRECOMPILED_RUNTIME)
     if (FLAG_print_llvm_constant_pool) {
@@ -923,7 +925,7 @@
   } else {
 #if !defined(TARGET_ARCH_IA32)
     if (I != Dart::vm_isolate()) {
-      I->object_store()->set_build_method_extractor_code(
+      IG->object_store()->set_build_method_extractor_code(
           Code::Handle(StubCode::GetBuildMethodExtractorStub(nullptr)));
     }
 #endif  // !defined(TARGET_ARCH_IA32)
@@ -933,7 +935,7 @@
 
   if ((snapshot_data == NULL) || (kernel_buffer != NULL)) {
     Error& error = Error::Handle();
-    error ^= I->object_store()->PreallocateObjects();
+    error ^= IG->object_store()->PreallocateObjects();
     if (!error.IsNull()) {
       return error.raw();
     }
@@ -944,11 +946,11 @@
   }
 
   if (!was_child_cloned_into_existing_isolate) {
-    I->heap()->InitGrowthControl();
+    IG->heap()->InitGrowthControl();
   }
   I->set_init_callback_data(isolate_data);
   if (FLAG_print_class_table) {
-    I->class_table()->Print();
+    IG->class_table()->Print();
   }
 #if !defined(PRODUCT)
   ServiceIsolate::MaybeMakeServiceIsolate(I);
@@ -981,6 +983,8 @@
 const char* Dart::FeaturesString(Isolate* isolate,
                                  bool is_vm_isolate,
                                  Snapshot::Kind kind) {
+  auto isolate_group = isolate != nullptr ? isolate->group() : nullptr;
+
   TextBuffer buffer(64);
 
 // Different fields are included for DEBUG/RELEASE/PRODUCT.
@@ -1001,21 +1005,22 @@
 #define ADD_C(name, PCV, PV, T, DV, C) ADD_FLAG(name, FLAG_##name)
 #define ADD_D(name, T, DV, C) ADD_FLAG(name, FLAG_##name)
 
-#define ADD_ISOLATE_FLAG(name, isolate_flag, flag)                             \
+#define ADD_ISOLATE_GROUP_FLAG(name, isolate_flag, flag)                       \
   do {                                                                         \
-    const bool value = (isolate != NULL) ? isolate->name() : flag;             \
+    const bool value =                                                         \
+        isolate_group != nullptr ? isolate_group->name() : flag;               \
     ADD_FLAG(#name, value);                                                    \
   } while (0);
 
   if (Snapshot::IncludesCode(kind)) {
     VM_GLOBAL_FLAG_LIST(ADD_P, ADD_R, ADD_C, ADD_D);
 
-    // enabling assertions affects deopt ids.
-    ADD_ISOLATE_FLAG(asserts, enable_asserts, FLAG_enable_asserts);
+    // Enabling assertions affects deopt ids.
+    ADD_ISOLATE_GROUP_FLAG(asserts, enable_asserts, FLAG_enable_asserts);
     if (kind == Snapshot::kFullJIT) {
-      ADD_ISOLATE_FLAG(use_field_guards, use_field_guards,
-                       FLAG_use_field_guards);
-      ADD_ISOLATE_FLAG(use_osr, use_osr, FLAG_use_osr);
+      ADD_ISOLATE_GROUP_FLAG(use_field_guards, use_field_guards,
+                             FLAG_use_field_guards);
+      ADD_ISOLATE_GROUP_FLAG(use_osr, use_osr, FLAG_use_osr);
     }
 #if !defined(PRODUCT)
     buffer.AddString(FLAG_code_comments ? " code-comments"
@@ -1053,7 +1058,6 @@
   }
 
   if (!Snapshot::IsAgnosticToNullSafety(kind)) {
-    auto isolate_group = isolate != nullptr ? isolate->group() : nullptr;
     if (isolate_group != nullptr) {
       if (isolate_group->null_safety()) {
         buffer.AddString(" null-safety");
diff --git a/runtime/vm/dart.h b/runtime/vm/dart.h
index 27ad70d..b7406c1 100644
--- a/runtime/vm/dart.h
+++ b/runtime/vm/dart.h
@@ -81,6 +81,7 @@
   static void ShutdownIsolate();
 
   static Isolate* vm_isolate() { return vm_isolate_; }
+  static IsolateGroup* vm_isolate_group() { return vm_isolate_->group(); }
   static ThreadPool* thread_pool() { return thread_pool_; }
   static bool VmIsolateNameEquals(const char* name);
 
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 1894eec..5353b50 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -147,7 +147,7 @@
 
 static InstancePtr GetListInstance(Zone* zone, const Object& obj) {
   if (obj.IsInstance()) {
-    ObjectStore* object_store = Isolate::Current()->object_store();
+    ObjectStore* object_store = IsolateGroup::Current()->object_store();
     const Type& list_rare_type =
         Type::Handle(zone, object_store->non_nullable_list_rare_type());
     ASSERT(!list_rare_type.IsNull());
@@ -164,7 +164,7 @@
 
 static InstancePtr GetMapInstance(Zone* zone, const Object& obj) {
   if (obj.IsInstance()) {
-    ObjectStore* object_store = Isolate::Current()->object_store();
+    ObjectStore* object_store = IsolateGroup::Current()->object_store();
     const Type& map_rare_type =
         Type::Handle(zone, object_store->non_nullable_map_rare_type());
     ASSERT(!map_rare_type.IsNull());
@@ -185,9 +185,9 @@
   // compiletime_error_class was removed.
   return false;
 #else
-  Isolate* I = Thread::Current()->isolate();
-  const Class& error_class =
-      Class::Handle(zone, I->object_store()->compiletime_error_class());
+  auto isolate_group = Thread::Current()->isolate_group();
+  const Class& error_class = Class::Handle(
+      zone, isolate_group->object_store()->compiletime_error_class());
   ASSERT(!error_class.IsNull());
   return (obj.GetClassId() == error_class.id());
 #endif
@@ -556,8 +556,8 @@
     return true;
   }
   if (cid == kOneByteStringCid || cid == kTwoByteStringCid) {
-    Isolate* isolate = arguments->thread()->isolate();
-    *peer = isolate->heap()->GetPeer(raw_obj);
+    auto isolate_group = arguments->thread()->isolate_group();
+    *peer = isolate_group->heap()->GetPeer(raw_obj);
     return (*peer != 0);
   }
   if (cid == kExternalTwoByteStringCid) {
@@ -655,7 +655,7 @@
   ObjectPtr raw_obj = arguments->NativeArgAt(arg_index);
   intptr_t cid = raw_obj->GetClassIdMayBeSmi();
   int class_num_fields = arguments->thread()
-                             ->isolate()
+                             ->isolate_group()
                              ->class_table()
                              ->At(cid)
                              ->ptr()
@@ -991,8 +991,8 @@
     return NULL;
   }
   FinalizablePersistentHandle* finalizable_ref =
-      FinalizablePersistentHandle::New(thread->isolate(), ref, peer, callback,
-                                       external_allocation_size,
+      FinalizablePersistentHandle::New(thread->isolate_group(), ref, peer,
+                                       callback, external_allocation_size,
                                        /*auto_delete=*/false);
   return finalizable_ref->ApiWeakPersistentHandle();
 }
@@ -1021,8 +1021,8 @@
   }
 
   FinalizablePersistentHandle* finalizable_ref =
-      FinalizablePersistentHandle::New(thread->isolate(), ref, peer, callback,
-                                       external_allocation_size,
+      FinalizablePersistentHandle::New(thread->isolate_group(), ref, peer,
+                                       callback, external_allocation_size,
                                        /*auto_delete=*/true);
   return finalizable_ref->ApiFinalizableHandle();
 }
@@ -1276,7 +1276,7 @@
 
   if (success) {
     if (is_new_group) {
-      I->heap()->InitGrowthControl();
+      group->heap()->InitGrowthControl();
     }
     // A Thread structure has been associated to the thread, we do the
     // safepoint transition explicitly here instead of using the
@@ -1825,7 +1825,7 @@
 #else
   DARTSCOPE(Thread::Current());
   API_TIMELINE_DURATION(T);
-  Isolate* I = T->isolate();
+  auto I = T->isolate();
   if (vm_snapshot_data_buffer != nullptr) {
     CHECK_NULL(vm_snapshot_data_size);
   }
@@ -1839,7 +1839,7 @@
   BackgroundCompiler::Stop(I);
 
 #if defined(DEBUG)
-  I->heap()->CollectAllGarbage();
+  T->isolate_group()->heap()->CollectAllGarbage();
   {
     HeapIterationScope iteration(T);
     CheckFunctionTypesVisitor check_canonical(T);
@@ -1966,7 +1966,7 @@
   }
   if (FLAG_print_class_table) {
     HANDLESCOPE(Thread::Current());
-    I->class_table()->Print();
+    I->group()->class_table()->Print();
   }
   return Api::Success();
 }
@@ -2448,7 +2448,7 @@
   API_TIMELINE_DURATION(T);
   const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle));
   if (obj.IsInstance()) {
-    ObjectStore* object_store = T->isolate()->object_store();
+    ObjectStore* object_store = T->isolate_group()->object_store();
     const Type& future_rare_type =
         Type::Handle(Z, object_store->non_nullable_future_rare_type());
     ASSERT(!future_rare_type.IsNull());
@@ -2466,10 +2466,10 @@
 DART_EXPORT Dart_Handle Dart_InstanceGetType(Dart_Handle instance) {
   DARTSCOPE(Thread::Current());
   API_TIMELINE_DURATION(T);
-  Isolate* I = T->isolate();
+  auto isolate_group = T->isolate_group();
   const Object& obj = Object::Handle(Z, Api::UnwrapHandle(instance));
   if (obj.IsNull()) {
-    return Api::NewHandle(T, I->object_store()->null_type());
+    return Api::NewHandle(T, isolate_group->object_store()->null_type());
   }
   if (!obj.IsInstance()) {
     RETURN_TYPE_ERROR(Z, instance, Instance);
@@ -3051,7 +3051,7 @@
     ASSERT(*peer != NULL);
   } else {
     NoSafepointScope no_safepoint_scope;
-    *peer = thread->isolate()->heap()->GetPeer(str.raw());
+    *peer = thread->heap()->GetPeer(str.raw());
   }
   *char_size = str.CharSize();
   *str_len = str.Length();
@@ -3093,7 +3093,7 @@
   const Array& arr = Array::Handle(Z, Array::New(length));
   if (element_type_id != Dart_CoreType_Dynamic) {
     arr.SetTypeArguments(TypeArguments::Handle(
-        Z, TypeArgumentsForElementType(T->isolate()->object_store(),
+        Z, TypeArgumentsForElementType(T->isolate_group()->object_store(),
                                        element_type_id)));
   }
   return Api::NewHandle(T, arr.raw());
@@ -3821,8 +3821,8 @@
 static ObjectPtr GetByteDataConstructor(Thread* thread,
                                         const String& constructor_name,
                                         intptr_t num_args) {
-  const Library& lib =
-      Library::Handle(thread->isolate()->object_store()->typed_data_library());
+  const Library& lib = Library::Handle(
+      thread->isolate_group()->object_store()->typed_data_library());
   ASSERT(!lib.IsNull());
   const Class& cls = Class::Handle(
       thread->zone(), lib.LookupClassAllowPrivate(Symbols::ByteData()));
@@ -3868,7 +3868,8 @@
   CHECK_LENGTH(length, ExternalTypedData::MaxElements(cid));
   Zone* zone = thread->zone();
   intptr_t bytes = length * ExternalTypedData::ElementSizeInBytes(cid);
-  auto& cls = Class::Handle(zone, thread->isolate()->class_table()->At(cid));
+  auto& cls =
+      Class::Handle(zone, thread->isolate_group()->class_table()->At(cid));
   auto& result = Object::Handle(zone, cls.EnsureIsAllocateFinalized(thread));
   if (result.IsError()) {
     return Api::NewHandle(thread, result.raw());
@@ -4058,8 +4059,8 @@
                                           const String& class_name,
                                           const String& constructor_name,
                                           intptr_t num_args) {
-  const Library& lib =
-      Library::Handle(thread->isolate()->object_store()->typed_data_library());
+  const Library& lib = Library::Handle(
+      thread->isolate_group()->object_store()->typed_data_library());
   ASSERT(!lib.IsNull());
   const Class& cls =
       Class::Handle(thread->zone(), lib.LookupClassAllowPrivate(class_name));
@@ -4193,9 +4194,9 @@
   }
   if (FLAG_verify_acquired_data) {
     if (external) {
-      ASSERT(!I->heap()->Contains(reinterpret_cast<uword>(data_tmp)));
+      ASSERT(!T->heap()->Contains(reinterpret_cast<uword>(data_tmp)));
     } else {
-      ASSERT(I->heap()->Contains(reinterpret_cast<uword>(data_tmp)));
+      ASSERT(T->heap()->Contains(reinterpret_cast<uword>(data_tmp)));
     }
     const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object));
     WeakTable* table = I->group()->api_state()->acquired_table();
@@ -5471,9 +5472,9 @@
   DARTSCOPE(Thread::Current());
   API_TIMELINE_DURATION(T);
   StackZone zone(T);
-  Isolate* I = T->isolate();
+  IsolateGroup* IG = T->isolate_group();
 
-  Library& library = Library::Handle(Z, I->object_store()->root_library());
+  Library& library = Library::Handle(Z, IG->object_store()->root_library());
   if (!library.IsNull()) {
     const String& library_url = String::Handle(Z, library.url());
     return Api::NewError("%s: A script has already been loaded from '%s'.",
@@ -5500,8 +5501,8 @@
     return Api::NewHandle(T, tmp.raw());
   }
 
-  I->source()->script_kernel_size = buffer_size;
-  I->source()->script_kernel_buffer = buffer;
+  IG->source()->script_kernel_size = buffer_size;
+  IG->source()->script_kernel_buffer = buffer;
 
   // TODO(32618): Setting root library based on whether it has 'main' or not
   // is not correct because main can be in the exported namespace of a library
@@ -5511,7 +5512,7 @@
                          CURRENT_FUNC);
   }
   library ^= tmp.raw();
-  I->object_store()->set_root_library(library);
+  IG->object_store()->set_root_library(library);
   return Api::NewHandle(T, library.raw());
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 }
@@ -5521,7 +5522,8 @@
   Isolate* isolate = thread->isolate();
   CHECK_ISOLATE(isolate);
   TransitionNativeToVM transition(thread);
-  return Api::NewHandle(thread, isolate->object_store()->root_library());
+  return Api::NewHandle(thread,
+                        isolate->group()->object_store()->root_library());
 }
 
 DART_EXPORT Dart_Handle Dart_SetRootLibrary(Dart_Handle library) {
@@ -5530,7 +5532,7 @@
   if (obj.IsNull() || obj.IsLibrary()) {
     Library& lib = Library::Handle(Z);
     lib ^= obj.raw();
-    T->isolate()->object_store()->set_root_library(lib);
+    T->isolate_group()->object_store()->set_root_library(lib);
     return library;
   }
   RETURN_TYPE_ERROR(Z, library, Library);
@@ -5740,10 +5742,10 @@
 
 DART_EXPORT Dart_Handle Dart_GetLoadedLibraries() {
   DARTSCOPE(Thread::Current());
-  Isolate* I = T->isolate();
+  auto IG = T->isolate_group();
 
   const GrowableObjectArray& libs =
-      GrowableObjectArray::Handle(Z, I->object_store()->libraries());
+      GrowableObjectArray::Handle(Z, IG->object_store()->libraries());
   int num_libs = libs.Length();
 
   // Create new list and populate with the loaded libraries.
@@ -5827,14 +5829,14 @@
 
 DART_EXPORT Dart_Handle Dart_GetImportsOfScheme(Dart_Handle scheme) {
   DARTSCOPE(Thread::Current());
-  Isolate* I = T->isolate();
+  auto IG = T->isolate_group();
   const String& scheme_vm = Api::UnwrapStringHandle(Z, scheme);
   if (scheme_vm.IsNull()) {
     RETURN_TYPE_ERROR(Z, scheme, String);
   }
 
   const GrowableObjectArray& libraries =
-      GrowableObjectArray::Handle(Z, I->object_store()->libraries());
+      GrowableObjectArray::Handle(Z, IG->object_store()->libraries());
   const GrowableObjectArray& result =
       GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
   Library& importer = Library::Handle(Z);
@@ -5888,7 +5890,7 @@
   // If this is an auxiliary isolate inside a larger isolate group, we will not
   // re-initialize the growth policy.
   if (I->group()->ContainsOnlyOneIsolate()) {
-    I->heap()->old_space()->EvaluateAfterLoading();
+    I->group()->heap()->old_space()->EvaluateAfterLoading();
   }
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
@@ -5913,11 +5915,11 @@
                                         bool transient_error) {
   DARTSCOPE(Thread::Current());
   API_TIMELINE_DURATION(T);
-  Isolate* I = T->isolate();
+  auto IG = T->isolate_group();
   CHECK_CALLBACK_STATE(T);
 
   const Array& loading_units =
-      Array::Handle(I->object_store()->loading_units());
+      Array::Handle(IG->object_store()->loading_units());
   if (loading_units.IsNull() || (loading_unit_id < LoadingUnit::kRootId) ||
       (loading_unit_id >= loading_units.Length())) {
     return Api::NewError("Invalid loading unit");
@@ -6041,7 +6043,7 @@
   {
     NoSafepointScope no_safepoint;
     ObjectPtr raw_obj = obj.raw();
-    *peer = thread->isolate()->heap()->GetPeer(raw_obj);
+    *peer = thread->heap()->GetPeer(raw_obj);
   }
   return Api::Success();
 }
@@ -6061,7 +6063,7 @@
   {
     NoSafepointScope no_safepoint;
     ObjectPtr raw_obj = obj.raw();
-    thread->isolate()->heap()->SetPeer(raw_obj, peer);
+    thread->heap()->SetPeer(raw_obj, peer);
   }
   return Api::Success();
 }
@@ -6424,7 +6426,7 @@
   CHECK_NULL(buffer);
   CHECK_NULL(buffer_length);
   CompilationTraceSaver saver(thread->zone());
-  ProgramVisitor::WalkProgram(thread->zone(), thread->isolate(), &saver);
+  ProgramVisitor::WalkProgram(thread->zone(), thread->isolate_group(), &saver);
   saver.StealBuffer(buffer, buffer_length);
   return Api::Success();
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
@@ -6446,7 +6448,7 @@
   saver.WriteHeader();
   saver.SaveClasses();
   saver.SaveFields();
-  ProgramVisitor::WalkProgram(thread->zone(), thread->isolate(), &saver);
+  ProgramVisitor::WalkProgram(thread->zone(), thread->isolate_group(), &saver);
   *buffer = stream.buffer();
   *buffer_length = stream.bytes_written();
 
@@ -6513,7 +6515,7 @@
   ClassFinalizer::ClearAllCode();
   // Make sure that ICData etc. that have been cleared are also removed from
   // the heap so that they are not found by the heap verifier.
-  Isolate::Current()->heap()->CollectAllGarbage();
+  IsolateGroup::Current()->heap()->CollectAllGarbage();
   ClassFinalizer::SortClasses();
   return Api::Success();
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
@@ -6636,7 +6638,7 @@
   ProgramVisitor::AssignUnits(T);
 
   const Array& loading_units =
-      Array::Handle(T->isolate()->object_store()->loading_units());
+      Array::Handle(T->isolate_group()->object_store()->loading_units());
   const uint32_t program_hash = ProgramVisitor::Hash(T);
   loading_units.SetAt(0, Smi::Handle(Z, Smi::New(program_hash)));
   GrowableArray<LoadingUnitSerializationData*> data;
@@ -6693,7 +6695,7 @@
   CHECK_NULL(callback);
 
   // Mark as not split.
-  T->isolate()->object_store()->set_loading_units(Object::null_array());
+  T->isolate_group()->object_store()->set_loading_units(Object::null_array());
 
   CreateAppAOTSnapshot(callback, callback_data, strip, /*as_elf*/ false,
                        debug_callback_data, nullptr, nullptr, 0);
@@ -6774,7 +6776,7 @@
   CHECK_NULL(callback);
 
   // Mark as not split.
-  T->isolate()->object_store()->set_loading_units(Object::null_array());
+  T->isolate_group()->object_store()->set_loading_units(Object::null_array());
 
   CreateAppAOTSnapshot(callback, callback_data, strip, /*as_elf*/ true,
                        debug_callback_data, nullptr, nullptr, 0);
@@ -6820,8 +6822,8 @@
 
   const GrowableObjectArray& result =
       GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
-  const GrowableObjectArray& libs =
-      GrowableObjectArray::Handle(Z, T->isolate()->object_store()->libraries());
+  const GrowableObjectArray& libs = GrowableObjectArray::Handle(
+      Z, T->isolate_group()->object_store()->libraries());
   Library& lib = Library::Handle(Z);
   LoadingUnit& unit = LoadingUnit::Handle(Z);
   String& uri = String::Handle(Z);
@@ -6986,7 +6988,8 @@
 #else
   DARTSCOPE(Thread::Current());
   API_TIMELINE_DURATION(T);
-  Isolate* I = T->isolate();
+  auto I = T->isolate();
+  auto IG = T->isolate_group();
   CHECK_NULL(isolate_snapshot_data_buffer);
   CHECK_NULL(isolate_snapshot_data_size);
   CHECK_NULL(isolate_snapshot_instructions_buffer);
@@ -7007,7 +7010,7 @@
   ProgramVisitor::Dedup(T);
 
   if (FLAG_dump_tables) {
-    Symbols::DumpTable(I);
+    Symbols::DumpTable(IG);
     DumpTypeTable(I);
     DumpTypeParameterTable(I);
     DumpTypeArgumentsTable(I);
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index 785a41d..cb554c8 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -3404,7 +3404,7 @@
 }
 
 TEST_CASE(DartAPI_WeakPersistentHandleCleanupFinalizer) {
-  Heap* heap = Isolate::Current()->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   const char* kTestString1 = "Test String1";
   Dart_EnterScope();
@@ -3444,7 +3444,7 @@
 }
 
 TEST_CASE(DartAPI_FinalizableHandleCleanupFinalizer) {
-  Heap* heap = Isolate::Current()->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   const char* kTestString1 = "Test String1";
   Dart_EnterScope();
@@ -3641,7 +3641,7 @@
 }
 
 TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSize) {
-  Heap* heap = Isolate::Current()->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
   EXPECT(heap->ExternalInWords(Heap::kNew) == 0);
   EXPECT(heap->ExternalInWords(Heap::kOld) == 0);
   Dart_WeakPersistentHandle weak1 = NULL;
@@ -3691,7 +3691,7 @@
 }
 
 TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSize) {
-  Heap* heap = Isolate::Current()->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
   EXPECT(heap->ExternalInWords(Heap::kNew) == 0);
   EXPECT(heap->ExternalInWords(Heap::kOld) == 0);
   static const intptr_t kWeak1ExternalSize = 1 * KB;
@@ -3733,7 +3733,7 @@
 }
 
 TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeNewspaceGC) {
-  Heap* heap = Isolate::Current()->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_WeakPersistentHandle weak1 = NULL;
   // Large enough to exceed any new space limit. Not actually allocated.
   const intptr_t kWeak1ExternalSize = 500 * MB;
@@ -3775,7 +3775,7 @@
 }
 
 TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSizeNewspaceGC) {
-  Heap* heap = Isolate::Current()->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_FinalizableHandle weak1 = nullptr;
   Dart_PersistentHandle strong1 = nullptr;
   // Large enough to exceed any new space limit. Not actually allocated.
@@ -3819,7 +3819,7 @@
 
 TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeOldspaceGC) {
   // Check that external allocation in old space can trigger GC.
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_EnterScope();
   Dart_Handle live = AllocateOldString("live");
   EXPECT_VALID(live);
@@ -3827,7 +3827,7 @@
   {
     TransitionNativeToVM transition(thread);
     GCTestHelper::WaitForGCTasks();  // Finalize GC for accurate live size.
-    EXPECT_EQ(0, isolate->heap()->ExternalInWords(Heap::kOld));
+    EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld));
   }
   const intptr_t kSmallExternalSize = 1 * KB;
   {
@@ -3843,7 +3843,7 @@
     TransitionNativeToVM transition(thread);
     GCTestHelper::WaitForGCTasks();  // Finalize GC for accurate live size.
     EXPECT_EQ(kSmallExternalSize,
-              isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
+              heap->ExternalInWords(Heap::kOld) * kWordSize);
   }
   // Large enough to trigger GC in old space. Not actually allocated.
   const intptr_t kHugeExternalSize = (kWordSize == 4) ? 513 * MB : 1025 * MB;
@@ -3853,8 +3853,7 @@
     TransitionNativeToVM transition(thread);
     GCTestHelper::WaitForGCTasks();  // Finalize GC for accurate live size.
     // Expect small garbage to be collected.
-    EXPECT_EQ(kHugeExternalSize,
-              isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
+    EXPECT_EQ(kHugeExternalSize, heap->ExternalInWords(Heap::kOld) * kWordSize);
   }
   Dart_ExitScope();
   Dart_DeleteWeakPersistentHandle(weak);
@@ -3863,14 +3862,14 @@
 
 TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSizeOldspaceGC) {
   // Check that external allocation in old space can trigger GC.
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_EnterScope();
   Dart_Handle live = AllocateOldString("live");
   EXPECT_VALID(live);
   {
     TransitionNativeToVM transition(thread);
     GCTestHelper::WaitForGCTasks();  // Finalize GC for accurate live size.
-    EXPECT_EQ(0, isolate->heap()->ExternalInWords(Heap::kOld));
+    EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld));
   }
   const intptr_t kSmallExternalSize = 1 * KB;
   {
@@ -3884,7 +3883,7 @@
     TransitionNativeToVM transition(thread);
     GCTestHelper::WaitForGCTasks();  // Finalize GC for accurate live size.
     EXPECT_EQ(kSmallExternalSize,
-              isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
+              heap->ExternalInWords(Heap::kOld) * kWordSize);
   }
   // Large enough to trigger GC in old space. Not actually allocated.
   const intptr_t kHugeExternalSize = (kWordSize == 4) ? 513 * MB : 1025 * MB;
@@ -3893,14 +3892,13 @@
     TransitionNativeToVM transition(thread);
     GCTestHelper::WaitForGCTasks();  // Finalize GC for accurate live size.
     // Expect small garbage to be collected.
-    EXPECT_EQ(kHugeExternalSize,
-              isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
+    EXPECT_EQ(kHugeExternalSize, heap->ExternalInWords(Heap::kOld) * kWordSize);
   }
   Dart_ExitScope();
 }
 
 TEST_CASE(DartAPI_WeakPersistentHandleExternalAllocationSizeOddReferents) {
-  Heap* heap = Isolate::Current()->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_WeakPersistentHandle weak1 = NULL;
   static const intptr_t kWeak1ExternalSize = 1 * KB;
   Dart_WeakPersistentHandle weak2 = NULL;
@@ -3934,7 +3932,7 @@
 }
 
 TEST_CASE(DartAPI_FinalizableHandleExternalAllocationSizeOddReferents) {
-  Heap* heap = Isolate::Current()->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_FinalizableHandle weak1 = nullptr;
   Dart_PersistentHandle strong1 = nullptr;
   static const intptr_t kWeak1ExternalSize = 1 * KB;
@@ -8281,17 +8279,17 @@
 // the peer and checks that the count of peer objects is decremented
 // by one.
 TEST_CASE(DartAPI_OneNewSpacePeer) {
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_Handle str = NewString("a string");
   EXPECT_VALID(str);
   EXPECT(Dart_IsString(str));
-  EXPECT_EQ(0, isolate->heap()->PeerCount());
+  EXPECT_EQ(0, heap->PeerCount());
   void* out = &out;
   EXPECT_VALID(Dart_GetPeer(str, &out));
   EXPECT(out == NULL);
   int peer = 1234;
   EXPECT_VALID(Dart_SetPeer(str, &peer));
-  EXPECT_EQ(1, isolate->heap()->PeerCount());
+  EXPECT_EQ(1, heap->PeerCount());
   out = &out;
   EXPECT_VALID(Dart_GetPeer(str, &out));
   EXPECT(out == reinterpret_cast<void*>(&peer));
@@ -8299,34 +8297,34 @@
   out = &out;
   EXPECT_VALID(Dart_GetPeer(str, &out));
   EXPECT(out == NULL);
-  EXPECT_EQ(0, isolate->heap()->PeerCount());
+  EXPECT_EQ(0, heap->PeerCount());
 }
 
 // Allocates an object in new space and assigns it a peer.  Allows the
 // peer referent to be garbage collected and checks that the count of
 // peer objects is decremented by one.
 TEST_CASE(DartAPI_CollectOneNewSpacePeer) {
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_EnterScope();
   {
     CHECK_API_SCOPE(thread);
     Dart_Handle str = NewString("a string");
     EXPECT_VALID(str);
     EXPECT(Dart_IsString(str));
-    EXPECT_EQ(0, isolate->heap()->PeerCount());
+    EXPECT_EQ(0, heap->PeerCount());
     void* out = &out;
     EXPECT_VALID(Dart_GetPeer(str, &out));
     EXPECT(out == NULL);
     int peer = 1234;
     EXPECT_VALID(Dart_SetPeer(str, &peer));
-    EXPECT_EQ(1, isolate->heap()->PeerCount());
+    EXPECT_EQ(1, heap->PeerCount());
     out = &out;
     EXPECT_VALID(Dart_GetPeer(str, &out));
     EXPECT(out == reinterpret_cast<void*>(&peer));
     {
       TransitionNativeToVM transition(thread);
       GCTestHelper::CollectNewSpace();
-      EXPECT_EQ(1, isolate->heap()->PeerCount());
+      EXPECT_EQ(1, heap->PeerCount());
     }
     out = &out;
     EXPECT_VALID(Dart_GetPeer(str, &out));
@@ -8336,7 +8334,7 @@
   {
     TransitionNativeToVM transition(thread);
     GCTestHelper::CollectNewSpace();
-    EXPECT_EQ(0, isolate->heap()->PeerCount());
+    EXPECT_EQ(0, heap->PeerCount());
   }
 }
 
@@ -8344,37 +8342,37 @@
 // the peers and checks that the count of peer objects is decremented
 // by two.
 TEST_CASE(DartAPI_TwoNewSpacePeers) {
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_Handle s1 = NewString("s1");
   EXPECT_VALID(s1);
   EXPECT(Dart_IsString(s1));
   void* o1 = &o1;
   EXPECT_VALID(Dart_GetPeer(s1, &o1));
   EXPECT(o1 == NULL);
-  EXPECT_EQ(0, isolate->heap()->PeerCount());
+  EXPECT_EQ(0, heap->PeerCount());
   int p1 = 1234;
   EXPECT_VALID(Dart_SetPeer(s1, &p1));
-  EXPECT_EQ(1, isolate->heap()->PeerCount());
+  EXPECT_EQ(1, heap->PeerCount());
   EXPECT_VALID(Dart_GetPeer(s1, &o1));
   EXPECT(o1 == reinterpret_cast<void*>(&p1));
   Dart_Handle s2 = NewString("a string");
   EXPECT_VALID(s2);
   EXPECT(Dart_IsString(s2));
-  EXPECT_EQ(1, isolate->heap()->PeerCount());
+  EXPECT_EQ(1, heap->PeerCount());
   void* o2 = &o2;
   EXPECT(Dart_GetPeer(s2, &o2));
   EXPECT(o2 == NULL);
   int p2 = 5678;
   EXPECT_VALID(Dart_SetPeer(s2, &p2));
-  EXPECT_EQ(2, isolate->heap()->PeerCount());
+  EXPECT_EQ(2, heap->PeerCount());
   EXPECT_VALID(Dart_GetPeer(s2, &o2));
   EXPECT(o2 == reinterpret_cast<void*>(&p2));
   EXPECT_VALID(Dart_SetPeer(s1, NULL));
-  EXPECT_EQ(1, isolate->heap()->PeerCount());
+  EXPECT_EQ(1, heap->PeerCount());
   EXPECT(Dart_GetPeer(s1, &o1));
   EXPECT(o1 == NULL);
   EXPECT_VALID(Dart_SetPeer(s2, NULL));
-  EXPECT_EQ(0, isolate->heap()->PeerCount());
+  EXPECT_EQ(0, heap->PeerCount());
   EXPECT(Dart_GetPeer(s2, &o2));
   EXPECT(o2 == NULL);
 }
@@ -8383,32 +8381,32 @@
 // the peer referents to be garbage collected and check that the count
 // of peer objects is decremented by two.
 TEST_CASE(DartAPI_CollectTwoNewSpacePeers) {
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_EnterScope();
   {
     CHECK_API_SCOPE(thread);
     Dart_Handle s1 = NewString("s1");
     EXPECT_VALID(s1);
     EXPECT(Dart_IsString(s1));
-    EXPECT_EQ(0, isolate->heap()->PeerCount());
+    EXPECT_EQ(0, heap->PeerCount());
     void* o1 = &o1;
     EXPECT(Dart_GetPeer(s1, &o1));
     EXPECT(o1 == NULL);
     int p1 = 1234;
     EXPECT_VALID(Dart_SetPeer(s1, &p1));
-    EXPECT_EQ(1, isolate->heap()->PeerCount());
+    EXPECT_EQ(1, heap->PeerCount());
     EXPECT_VALID(Dart_GetPeer(s1, &o1));
     EXPECT(o1 == reinterpret_cast<void*>(&p1));
     Dart_Handle s2 = NewString("s2");
     EXPECT_VALID(s2);
     EXPECT(Dart_IsString(s2));
-    EXPECT_EQ(1, isolate->heap()->PeerCount());
+    EXPECT_EQ(1, heap->PeerCount());
     void* o2 = &o2;
     EXPECT(Dart_GetPeer(s2, &o2));
     EXPECT(o2 == NULL);
     int p2 = 5678;
     EXPECT_VALID(Dart_SetPeer(s2, &p2));
-    EXPECT_EQ(2, isolate->heap()->PeerCount());
+    EXPECT_EQ(2, heap->PeerCount());
     EXPECT_VALID(Dart_GetPeer(s2, &o2));
     EXPECT(o2 == reinterpret_cast<void*>(&p2));
   }
@@ -8416,7 +8414,7 @@
   {
     TransitionNativeToVM transition(thread);
     GCTestHelper::CollectNewSpace();
-    EXPECT_EQ(0, isolate->heap()->PeerCount());
+    EXPECT_EQ(0, heap->PeerCount());
   }
 }
 
@@ -8424,7 +8422,7 @@
 // garbage collections and checks that the peer count is stable.
 TEST_CASE(DartAPI_CopyNewSpacePeers) {
   const int kPeerCount = 10;
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_Handle s[kPeerCount];
   for (int i = 0; i < kPeerCount; ++i) {
     s[i] = NewString("a string");
@@ -8434,22 +8432,22 @@
     EXPECT_VALID(Dart_GetPeer(s[i], &o));
     EXPECT(o == NULL);
   }
-  EXPECT_EQ(0, isolate->heap()->PeerCount());
+  EXPECT_EQ(0, heap->PeerCount());
   int p[kPeerCount];
   for (int i = 0; i < kPeerCount; ++i) {
     EXPECT_VALID(Dart_SetPeer(s[i], &p[i]));
-    EXPECT_EQ(i + 1, isolate->heap()->PeerCount());
+    EXPECT_EQ(i + 1, heap->PeerCount());
     void* o = &o;
     EXPECT_VALID(Dart_GetPeer(s[i], &o));
     EXPECT(o == reinterpret_cast<void*>(&p[i]));
   }
-  EXPECT_EQ(kPeerCount, isolate->heap()->PeerCount());
+  EXPECT_EQ(kPeerCount, heap->PeerCount());
   {
     TransitionNativeToVM transition(thread);
     GCTestHelper::CollectNewSpace();
-    EXPECT_EQ(kPeerCount, isolate->heap()->PeerCount());
+    EXPECT_EQ(kPeerCount, heap->PeerCount());
     GCTestHelper::CollectNewSpace();
-    EXPECT_EQ(kPeerCount, isolate->heap()->PeerCount());
+    EXPECT_EQ(kPeerCount, heap->PeerCount());
   }
 }
 
@@ -8457,11 +8455,11 @@
 // the peer to old space.  Removes the peer and check that the count
 // of peer objects is decremented by one.
 TEST_CASE(DartAPI_OnePromotedPeer) {
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_Handle str = NewString("a string");
   EXPECT_VALID(str);
   EXPECT(Dart_IsString(str));
-  EXPECT_EQ(0, isolate->heap()->PeerCount());
+  EXPECT_EQ(0, heap->PeerCount());
   void* out = &out;
   EXPECT(Dart_GetPeer(str, &out));
   EXPECT(out == NULL);
@@ -8470,7 +8468,7 @@
   out = &out;
   EXPECT(Dart_GetPeer(str, &out));
   EXPECT(out == reinterpret_cast<void*>(&peer));
-  EXPECT_EQ(1, isolate->heap()->PeerCount());
+  EXPECT_EQ(1, heap->PeerCount());
   {
     TransitionNativeToVM transition(thread);
     GCTestHelper::CollectNewSpace();
@@ -8486,36 +8484,36 @@
   }
   EXPECT_VALID(Dart_GetPeer(str, &out));
   EXPECT(out == reinterpret_cast<void*>(&peer));
-  EXPECT_EQ(1, isolate->heap()->PeerCount());
+  EXPECT_EQ(1, heap->PeerCount());
   EXPECT_VALID(Dart_SetPeer(str, NULL));
   out = &out;
   EXPECT_VALID(Dart_GetPeer(str, &out));
   EXPECT(out == NULL);
-  EXPECT_EQ(0, isolate->heap()->PeerCount());
+  EXPECT_EQ(0, heap->PeerCount());
 }
 
 // Allocates an object in old space and assigns it a peer.  Removes
 // the peer and checks that the count of peer objects is decremented
 // by one.
 TEST_CASE(DartAPI_OneOldSpacePeer) {
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_Handle str = AllocateOldString("str");
   EXPECT_VALID(str);
   EXPECT(Dart_IsString(str));
-  EXPECT_EQ(0, isolate->heap()->PeerCount());
+  EXPECT_EQ(0, heap->PeerCount());
   void* out = &out;
   EXPECT(Dart_GetPeer(str, &out));
   EXPECT(out == NULL);
   int peer = 1234;
   EXPECT_VALID(Dart_SetPeer(str, &peer));
-  EXPECT_EQ(1, isolate->heap()->PeerCount());
+  EXPECT_EQ(1, heap->PeerCount());
   out = &out;
   EXPECT_VALID(Dart_GetPeer(str, &out));
   EXPECT(out == reinterpret_cast<void*>(&peer));
   {
     TransitionNativeToVM transition(thread);
     GCTestHelper::CollectOldSpace();
-    EXPECT_EQ(1, isolate->heap()->PeerCount());
+    EXPECT_EQ(1, heap->PeerCount());
   }
   EXPECT_VALID(Dart_GetPeer(str, &out));
   EXPECT(out == reinterpret_cast<void*>(&peer));
@@ -8523,14 +8521,14 @@
   out = &out;
   EXPECT_VALID(Dart_GetPeer(str, &out));
   EXPECT(out == NULL);
-  EXPECT_EQ(0, isolate->heap()->PeerCount());
+  EXPECT_EQ(0, heap->PeerCount());
 }
 
 // Allocates an object in old space and assigns it a peer.  Allow the
 // peer referent to be garbage collected and check that the count of
 // peer objects is decremented by one.
 TEST_CASE(DartAPI_CollectOneOldSpacePeer) {
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_EnterScope();
   {
     Thread* T = Thread::Current();
@@ -8538,20 +8536,20 @@
     Dart_Handle str = AllocateOldString("str");
     EXPECT_VALID(str);
     EXPECT(Dart_IsString(str));
-    EXPECT_EQ(0, isolate->heap()->PeerCount());
+    EXPECT_EQ(0, heap->PeerCount());
     void* out = &out;
     EXPECT(Dart_GetPeer(str, &out));
     EXPECT(out == NULL);
     int peer = 1234;
     EXPECT_VALID(Dart_SetPeer(str, &peer));
-    EXPECT_EQ(1, isolate->heap()->PeerCount());
+    EXPECT_EQ(1, heap->PeerCount());
     out = &out;
     EXPECT_VALID(Dart_GetPeer(str, &out));
     EXPECT(out == reinterpret_cast<void*>(&peer));
     {
       TransitionNativeToVM transition(thread);
       GCTestHelper::CollectOldSpace();
-      EXPECT_EQ(1, isolate->heap()->PeerCount());
+      EXPECT_EQ(1, heap->PeerCount());
     }
     EXPECT_VALID(Dart_GetPeer(str, &out));
     EXPECT(out == reinterpret_cast<void*>(&peer));
@@ -8560,7 +8558,7 @@
   {
     TransitionNativeToVM transition(thread);
     GCTestHelper::CollectOldSpace();
-    EXPECT_EQ(0, isolate->heap()->PeerCount());
+    EXPECT_EQ(0, heap->PeerCount());
   }
 }
 
@@ -8568,40 +8566,40 @@
 // the peers and checks that the count of peer objects is decremented
 // by two.
 TEST_CASE(DartAPI_TwoOldSpacePeers) {
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_Handle s1 = AllocateOldString("s1");
   EXPECT_VALID(s1);
   EXPECT(Dart_IsString(s1));
-  EXPECT_EQ(0, isolate->heap()->PeerCount());
+  EXPECT_EQ(0, heap->PeerCount());
   void* o1 = &o1;
   EXPECT(Dart_GetPeer(s1, &o1));
   EXPECT(o1 == NULL);
   int p1 = 1234;
   EXPECT_VALID(Dart_SetPeer(s1, &p1));
-  EXPECT_EQ(1, isolate->heap()->PeerCount());
+  EXPECT_EQ(1, heap->PeerCount());
   o1 = &o1;
   EXPECT_VALID(Dart_GetPeer(s1, &o1));
   EXPECT(o1 == reinterpret_cast<void*>(&p1));
   Dart_Handle s2 = AllocateOldString("s2");
   EXPECT_VALID(s2);
   EXPECT(Dart_IsString(s2));
-  EXPECT_EQ(1, isolate->heap()->PeerCount());
+  EXPECT_EQ(1, heap->PeerCount());
   void* o2 = &o2;
   EXPECT(Dart_GetPeer(s2, &o2));
   EXPECT(o2 == NULL);
   int p2 = 5678;
   EXPECT_VALID(Dart_SetPeer(s2, &p2));
-  EXPECT_EQ(2, isolate->heap()->PeerCount());
+  EXPECT_EQ(2, heap->PeerCount());
   o2 = &o2;
   EXPECT_VALID(Dart_GetPeer(s2, &o2));
   EXPECT(o2 == reinterpret_cast<void*>(&p2));
   EXPECT_VALID(Dart_SetPeer(s1, NULL));
-  EXPECT_EQ(1, isolate->heap()->PeerCount());
+  EXPECT_EQ(1, heap->PeerCount());
   o1 = &o1;
   EXPECT(Dart_GetPeer(s1, &o1));
   EXPECT(o1 == NULL);
   EXPECT_VALID(Dart_SetPeer(s2, NULL));
-  EXPECT_EQ(0, isolate->heap()->PeerCount());
+  EXPECT_EQ(0, heap->PeerCount());
   o2 = &o2;
   EXPECT_VALID(Dart_GetPeer(s2, &o2));
   EXPECT(o2 == NULL);
@@ -8611,7 +8609,7 @@
 // the peer referents to be garbage collected and checks that the
 // count of peer objects is decremented by two.
 TEST_CASE(DartAPI_CollectTwoOldSpacePeers) {
-  Isolate* isolate = Isolate::Current();
+  Heap* heap = IsolateGroup::Current()->heap();
   Dart_EnterScope();
   {
     Thread* T = Thread::Current();
@@ -8619,26 +8617,26 @@
     Dart_Handle s1 = AllocateOldString("s1");
     EXPECT_VALID(s1);
     EXPECT(Dart_IsString(s1));
-    EXPECT_EQ(0, isolate->heap()->PeerCount());
+    EXPECT_EQ(0, heap->PeerCount());
     void* o1 = &o1;
     EXPECT(Dart_GetPeer(s1, &o1));
     EXPECT(o1 == NULL);
     int p1 = 1234;
     EXPECT_VALID(Dart_SetPeer(s1, &p1));
-    EXPECT_EQ(1, isolate->heap()->PeerCount());
+    EXPECT_EQ(1, heap->PeerCount());
     o1 = &o1;
     EXPECT_VALID(Dart_GetPeer(s1, &o1));
     EXPECT(o1 == reinterpret_cast<void*>(&p1));
     Dart_Handle s2 = AllocateOldString("s2");
     EXPECT_VALID(s2);
     EXPECT(Dart_IsString(s2));
-    EXPECT_EQ(1, isolate->heap()->PeerCount());
+    EXPECT_EQ(1, heap->PeerCount());
     void* o2 = &o2;
     EXPECT(Dart_GetPeer(s2, &o2));
     EXPECT(o2 == NULL);
     int p2 = 5678;
     EXPECT_VALID(Dart_SetPeer(s2, &p2));
-    EXPECT_EQ(2, isolate->heap()->PeerCount());
+    EXPECT_EQ(2, heap->PeerCount());
     o2 = &o2;
     EXPECT_VALID(Dart_GetPeer(s2, &o2));
     EXPECT(o2 == reinterpret_cast<void*>(&p2));
@@ -8647,7 +8645,7 @@
   {
     TransitionNativeToVM transition(thread);
     GCTestHelper::CollectOldSpace();
-    EXPECT_EQ(0, isolate->heap()->PeerCount());
+    EXPECT_EQ(0, heap->PeerCount());
   }
 }
 
diff --git a/runtime/vm/dart_api_state.h b/runtime/vm/dart_api_state.h
index b9ba995..1757cce 100644
--- a/runtime/vm/dart_api_state.h
+++ b/runtime/vm/dart_api_state.h
@@ -190,7 +190,7 @@
 // dart API.
 class FinalizablePersistentHandle {
  public:
-  static FinalizablePersistentHandle* New(Isolate* isolate,
+  static FinalizablePersistentHandle* New(IsolateGroup* isolate_group,
                                           const Object& object,
                                           void* peer,
                                           Dart_HandleFinalizer callback,
@@ -864,13 +864,13 @@
 };
 
 inline FinalizablePersistentHandle* FinalizablePersistentHandle::New(
-    Isolate* isolate,
+    IsolateGroup* isolate_group,
     const Object& object,
     void* peer,
     Dart_HandleFinalizer callback,
     intptr_t external_size,
     bool auto_delete) {
-  ApiState* state = isolate->group()->api_state();
+  ApiState* state = isolate_group->api_state();
   ASSERT(state != NULL);
   FinalizablePersistentHandle* ref = state->AllocateWeakPersistentHandle();
   ref->set_raw(object);
@@ -878,7 +878,7 @@
   ref->set_callback(callback);
   ref->set_auto_delete(auto_delete);
   // This may trigger GC, so it must be called last.
-  ref->SetExternalSize(external_size, isolate->group());
+  ref->SetExternalSize(external_size, isolate_group);
   return ref;
 }
 
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc
index 1638e3e..0d01009 100644
--- a/runtime/vm/dart_entry.cc
+++ b/runtime/vm/dart_entry.cc
@@ -110,7 +110,7 @@
     if (FLAG_use_bare_instructions) {
       Thread* thread = Thread::Current();
       thread->set_global_object_pool(
-          thread->isolate()->object_store()->global_object_pool());
+          thread->isolate_group()->object_store()->global_object_pool());
       const DispatchTable* dispatch_table = thread->isolate()->dispatch_table();
       if (dispatch_table != nullptr) {
         thread->set_dispatch_table_array(dispatch_table->ArrayOrigin());
@@ -185,7 +185,7 @@
 ObjectPtr DartEntry::ResolveCallable(Thread* thread,
                                      const Array& arguments,
                                      const Array& arguments_descriptor) {
-  auto isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   auto zone = thread->zone();
 
   const ArgumentsDescriptor args_desc(arguments_descriptor);
@@ -229,8 +229,8 @@
       break;
     }
     if (!OSThread::Current()->HasStackHeadroom()) {
-      const Instance& exception =
-          Instance::Handle(zone, isolate->object_store()->stack_overflow());
+      const Instance& exception = Instance::Handle(
+          zone, isolate_group->object_store()->stack_overflow());
       return UnhandledException::New(exception, StackTrace::Handle(zone));
     }
 
@@ -355,7 +355,8 @@
     ASSERT(!FLAG_lazy_dispatchers);
     // If noSuchMethod(invocation) is not found, call Object::noSuchMethod.
     function = Resolver::ResolveDynamicForReceiverClass(
-        Class::Handle(zone, thread->isolate()->object_store()->object_class()),
+        Class::Handle(zone,
+                      thread->isolate_group()->object_store()->object_class()),
         Symbols::NoSuchMethod(), nsm_args_desc);
   }
   ASSERT(!function.IsNull());
@@ -695,7 +696,7 @@
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
   Function& function = Function::Handle(
-      zone, thread->isolate()->object_store()->lookup_port_handler());
+      zone, thread->isolate_group()->object_store()->lookup_port_handler());
   const int kTypeArgsLen = 0;
   const int kNumArguments = 1;
   if (function.IsNull()) {
@@ -709,7 +710,7 @@
                                        kTypeArgsLen, kNumArguments,
                                        Object::empty_array());
     ASSERT(!function.IsNull());
-    thread->isolate()->object_store()->set_lookup_port_handler(function);
+    thread->isolate_group()->object_store()->set_lookup_port_handler(function);
   }
   Array& args = Array::Handle(
       zone, thread->isolate()->isolate_object_store()->dart_args_1());
@@ -727,7 +728,7 @@
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
   Function& function = Function::Handle(
-      zone, thread->isolate()->object_store()->lookup_open_ports());
+      zone, thread->isolate_group()->object_store()->lookup_open_ports());
   const int kTypeArgsLen = 0;
   const int kNumArguments = 0;
   if (function.IsNull()) {
@@ -741,7 +742,7 @@
                                        kTypeArgsLen, kNumArguments,
                                        Object::empty_array());
     ASSERT(!function.IsNull());
-    thread->isolate()->object_store()->set_lookup_open_ports(function);
+    thread->isolate_group()->object_store()->set_lookup_open_ports(function);
   }
   const Object& result = Object::Handle(
       zone, DartEntry::InvokeFunction(function, Object::empty_array()));
@@ -750,11 +751,12 @@
 
 ObjectPtr DartLibraryCalls::HandleMessage(const Object& handler,
                                           const Instance& message) {
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
-  Function& function = Function::Handle(
-      zone, isolate->object_store()->handle_message_function());
+  auto thread = Thread::Current();
+  auto zone = thread->zone();
+  auto isolate = thread->isolate();
+  auto object_store = thread->isolate_group()->object_store();
+  Function& function =
+      Function::Handle(zone, object_store->handle_message_function());
   const int kTypeArgsLen = 0;
   const int kNumArguments = 2;
   if (function.IsNull()) {
@@ -768,13 +770,13 @@
                                        kTypeArgsLen, kNumArguments,
                                        Object::empty_array());
     ASSERT(!function.IsNull());
-    isolate->object_store()->set_handle_message_function(function);
+    object_store->set_handle_message_function(function);
   }
-  Array& args = Array::Handle(
-      zone, thread->isolate()->isolate_object_store()->dart_args_2());
+  Array& args =
+      Array::Handle(zone, isolate->isolate_object_store()->dart_args_2());
   if (args.IsNull()) {
     args = Array::New(kNumArguments);
-    thread->isolate()->isolate_object_store()->set_dart_args_2(args);
+    isolate->isolate_object_store()->set_dart_args_2(args);
   }
   args.SetAt(0, handler);
   args.SetAt(1, message);
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 5db0e49..b2f3baf 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -826,13 +826,14 @@
 
 bool ActivationFrame::IsAsyncMachinery() const {
   ASSERT(!function_.IsNull());
-  Isolate* isolate = Isolate::Current();
-  if (function_.raw() == isolate->object_store()->complete_on_async_return()) {
+  auto isolate_group = IsolateGroup::Current();
+  if (function_.raw() ==
+      isolate_group->object_store()->complete_on_async_return()) {
     // We are completing an async function's completer.
     return true;
   }
   if (function_.Owner() ==
-      isolate->object_store()->async_star_stream_controller()) {
+      isolate_group->object_store()->async_star_stream_controller()) {
     // We are inside the async* stream controller code.
     return true;
   }
@@ -1617,9 +1618,10 @@
 
   // Iterate over all classes, deoptimize functions.
   // TODO(hausner): Could possibly be combined with RemoveOptimizedCode()
-  const ClassTable& class_table = *isolate_->class_table();
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
+  const ClassTable& class_table = *isolate_->group()->class_table();
+  auto thread = Thread::Current();
+  auto isolate_group = thread->isolate_group();
+  auto zone = thread->zone();
   CallSiteResetter resetter(zone);
   Class& cls = Class::Handle(zone);
   Array& functions = Array::Handle(zone);
@@ -1675,7 +1677,7 @@
   }
 
   // Disable optimized closure functions.
-  closures = isolate_->object_store()->closure_functions();
+  closures = isolate_group->object_store()->closure_functions();
   const intptr_t num_closures = closures.Length();
   for (intptr_t pos = 0; pos < num_closures; pos++) {
     function ^= closures.At(pos);
@@ -2478,14 +2480,15 @@
     TokenPosition start_pos,
     TokenPosition end_pos,
     GrowableObjectArray* code_function_list) {
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
+  auto thread = Thread::Current();
+  auto isolate_group = thread->isolate_group();
+  auto zone = thread->zone();
   Class& cls = Class::Handle(zone);
   Array& functions = Array::Handle(zone);
   GrowableObjectArray& closures = GrowableObjectArray::Handle(zone);
   Function& function = Function::Handle(zone);
 
-  closures = isolate_->object_store()->closure_functions();
+  closures = isolate_group->object_store()->closure_functions();
   const intptr_t num_closures = closures.Length();
   for (intptr_t pos = 0; pos < num_closures; pos++) {
     function ^= closures.At(pos);
@@ -2509,7 +2512,7 @@
     }
   }
 
-  const ClassTable& class_table = *isolate_->class_table();
+  const ClassTable& class_table = *isolate_->group()->class_table();
   const intptr_t num_classes = class_table.NumCids();
   const intptr_t num_tlc_classes = class_table.NumTopLevelCids();
   for (intptr_t i = 1; i < num_classes + num_tlc_classes; i++) {
@@ -2572,7 +2575,8 @@
                            TokenPosition token_pos,
                            TokenPosition last_token_pos,
                            Function* best_fit) {
-  Thread* thread = Thread::Current();
+  auto thread = Thread::Current();
+  auto isolate_group = thread->isolate_group();
   Zone* zone = thread->zone();
   Class& cls = Class::Handle(zone);
 
@@ -2582,7 +2586,7 @@
   // Return the first fit found, but if a library doesn't contain a fit,
   // process the next one.
   const GrowableObjectArray& libs = GrowableObjectArray::Handle(
-      zone, thread->isolate()->object_store()->libraries());
+      zone, isolate_group->object_store()->libraries());
   Library& lib = Library::Handle(zone);
   for (int i = 0; i < libs.Length(); i++) {
     lib ^= libs.At(i);
@@ -2607,7 +2611,7 @@
       continue;
     }
     const GrowableObjectArray& closures = GrowableObjectArray::Handle(
-        zone, isolate_->object_store()->closure_functions());
+        zone, isolate_group->object_store()->closure_functions());
     Array& functions = Array::Handle(zone);
     Function& function = Function::Handle(zone);
     Array& fields = Array::Handle(zone);
@@ -2629,7 +2633,7 @@
       return true;
     }
 
-    const ClassTable& class_table = *isolate_->class_table();
+    const ClassTable& class_table = *isolate_->group()->class_table();
     const intptr_t num_classes = class_table.NumCids();
     const intptr_t num_tlc_classes = class_table.NumTopLevelCids();
     for (intptr_t i = 1; i < num_classes + num_tlc_classes; i++) {
@@ -2970,8 +2974,8 @@
   Zone* zone = Thread::Current()->zone();
   Library& lib = Library::Handle(zone);
   Script& script = Script::Handle(zone);
-  const GrowableObjectArray& libs =
-      GrowableObjectArray::Handle(isolate_->object_store()->libraries());
+  const GrowableObjectArray& libs = GrowableObjectArray::Handle(
+      isolate_->group()->object_store()->libraries());
   bool is_package = script_url.StartsWith(Symbols::PackageScheme());
   Script& script_for_lib = Script::Handle(zone);
   for (intptr_t i = 0; i < libs.Length(); i++) {
@@ -3824,10 +3828,12 @@
                                            TokenPosition token_pos) {
   ASSERT(function.end_token_pos().IsReal());
   const TokenPosition& func_start = function.token_pos();
-  Zone* zone = Thread::Current()->zone();
+  auto thread = Thread::Current();
+  auto zone = thread->zone();
+  auto isolate_group = thread->isolate_group();
   const Script& outer_origin = Script::Handle(zone, function.script());
   const GrowableObjectArray& closures = GrowableObjectArray::Handle(
-      zone, Isolate::Current()->object_store()->closure_functions());
+      zone, isolate_group->object_store()->closure_functions());
   const intptr_t num_closures = closures.Length();
   Function& closure = Function::Handle(zone);
   Function& best_fit = Function::Handle(zone);
@@ -3960,14 +3966,16 @@
     // Common, fast path.
     return;
   }
-  Zone* zone = Thread::Current()->zone();
+  auto thread = Thread::Current();
+  auto isolate_group = thread->isolate_group();
+  auto zone = thread->zone();
   Library& lib = Library::Handle(zone);
   Script& script = Script::Handle(zone);
   String& url = String::Handle(zone);
   BreakpointLocation* loc = latent_locations_;
   BreakpointLocation* prev_loc = NULL;
   const GrowableObjectArray& libs =
-      GrowableObjectArray::Handle(isolate_->object_store()->libraries());
+      GrowableObjectArray::Handle(isolate_group->object_store()->libraries());
   while (loc != NULL) {
     url = loc->url();
     bool found_match = false;
diff --git a/runtime/vm/deferred_objects.cc b/runtime/vm/deferred_objects.cc
index a510fe0..4d52f84 100644
--- a/runtime/vm/deferred_objects.cc
+++ b/runtime/vm/deferred_objects.cc
@@ -87,7 +87,7 @@
   DeferredObject* obj = deopt_context->GetDeferredObject(index());
   *slot() = obj->object();
   if (FLAG_trace_deoptimization_verbose) {
-    const Class& cls = Class::Handle(Isolate::Current()->class_table()->At(
+    const Class& cls = Class::Handle(IsolateGroup::Current()->class_table()->At(
         Object::Handle(obj->object()).GetClassId()));
     OS::PrintErr("writing instance of class %s ref at %" Px ".\n",
                  cls.ToCString(), reinterpret_cast<uword>(slot()));
diff --git a/runtime/vm/dwarf.cc b/runtime/vm/dwarf.cc
index a98dae0..8df88dd 100644
--- a/runtime/vm/dwarf.cc
+++ b/runtime/vm/dwarf.cc
@@ -289,7 +289,7 @@
   // them in our abbreviation above in WriteAbbreviations.
   stream->uleb128(kCompilationUnit);
   const Library& root_library = Library::Handle(
-      zone_, Isolate::Current()->object_store()->root_library());
+      zone_, IsolateGroup::Current()->object_store()->root_library());
   const String& root_uri = String::Handle(zone_, root_library.url());
   stream->string(root_uri.ToCString());  // DW_AT_name
   stream->string("Dart VM");             // DW_AT_producer
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index 20edf4d..c0fc071 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -719,14 +719,14 @@
   }
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   Class& error_class =
-      Class::Handle(zone, isolate->object_store()->error_class());
+      Class::Handle(zone, isolate_group->object_store()->error_class());
   if (error_class.IsNull()) {
     const Library& core_lib = Library::Handle(zone, Library::CoreLibrary());
     error_class = core_lib.LookupClass(Symbols::Error());
     ASSERT(!error_class.IsNull());
-    isolate->object_store()->set_error_class(error_class);
+    isolate_group->object_store()->set_error_class(error_class);
   }
   // If instance class extends 'class Error' return '_stackTrace' field.
   Class& test_class = Class::Handle(zone, instance.clazz());
@@ -758,13 +758,14 @@
   // should jump there instead.
   RELEASE_ASSERT(thread->long_jump_base() == nullptr);
   Zone* zone = thread->zone();
+  auto object_store = thread->isolate_group()->object_store();
   Isolate* isolate = thread->isolate();
 #if !defined(PRODUCT)
   // Do not notify debugger on stack overflow and out of memory exceptions.
   // The VM would crash when the debugger calls back into the VM to
   // get values of variables.
-  if (incoming_exception.raw() != isolate->object_store()->out_of_memory() &&
-      incoming_exception.raw() != isolate->object_store()->stack_overflow()) {
+  if (incoming_exception.raw() != object_store->out_of_memory() &&
+      incoming_exception.raw() != object_store->stack_overflow()) {
     isolate->debugger()->PauseException(incoming_exception);
   }
 #endif
@@ -773,8 +774,8 @@
   if (exception.IsNull()) {
     exception ^=
         Exceptions::Create(Exceptions::kNullThrown, Object::empty_array());
-  } else if (exception.raw() == isolate->object_store()->out_of_memory() ||
-             exception.raw() == isolate->object_store()->stack_overflow()) {
+  } else if (exception.raw() == object_store->out_of_memory() ||
+             exception.raw() == object_store->stack_overflow()) {
     use_preallocated_stacktrace = true;
   }
   // Find the exception handler and determine if the handler needs a
@@ -789,8 +790,7 @@
   if (use_preallocated_stacktrace) {
     if (handler_pc == 0) {
       // No Dart frame.
-      ASSERT(incoming_exception.raw() ==
-             isolate->object_store()->out_of_memory());
+      ASSERT(incoming_exception.raw() == object_store->out_of_memory());
       const UnhandledException& error = UnhandledException::Handle(
           zone,
           isolate->isolate_object_store()->preallocated_unhandled_exception());
@@ -855,7 +855,7 @@
     // the isolate etc.). This can happen in the compiler, which is not
     // allowed to allocate in new space, so we pass the kOld argument.
     const UnhandledException& unhandled_exception = UnhandledException::Handle(
-        zone, exception.raw() == isolate->object_store()->out_of_memory()
+        zone, exception.raw() == object_store->out_of_memory()
                   ? isolate->isolate_object_store()
                         ->preallocated_unhandled_exception()
                   : UnhandledException::New(exception, stacktrace, Heap::kOld));
@@ -1059,18 +1059,18 @@
 }
 
 void Exceptions::ThrowOOM() {
-  Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
+  auto thread = Thread::Current();
+  auto isolate_group = thread->isolate_group();
   const Instance& oom = Instance::Handle(
-      thread->zone(), isolate->object_store()->out_of_memory());
+      thread->zone(), isolate_group->object_store()->out_of_memory());
   Throw(thread, oom);
 }
 
 void Exceptions::ThrowStackOverflow() {
-  Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
+  auto thread = Thread::Current();
+  auto isolate_group = thread->isolate_group();
   const Instance& stack_overflow = Instance::Handle(
-      thread->zone(), isolate->object_store()->stack_overflow());
+      thread->zone(), isolate_group->object_store()->stack_overflow());
   Throw(thread, stack_overflow);
 }
 
diff --git a/runtime/vm/heap/become_test.cc b/runtime/vm/heap/become_test.cc
index b3e89220..7834a2e 100644
--- a/runtime/vm/heap/become_test.cc
+++ b/runtime/vm/heap/become_test.cc
@@ -50,8 +50,7 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(BecomeForwardPeer) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   const Array& before_obj = Array::Handle(Array::New(0, Heap::kOld));
   const Array& after_obj = Array::Handle(Array::New(0, Heap::kOld));
diff --git a/runtime/vm/heap/compactor.cc b/runtime/vm/heap/compactor.cc
index cd6e83f..e60eed9 100644
--- a/runtime/vm/heap/compactor.cc
+++ b/runtime/vm/heap/compactor.cc
@@ -573,7 +573,8 @@
 void GCCompactor::SetupImagePageBoundaries() {
   MallocGrowableArray<ImagePageRange> ranges(4);
 
-  OldPage* image_page = Dart::vm_isolate()->heap()->old_space()->image_pages_;
+  OldPage* image_page =
+      Dart::vm_isolate_group()->heap()->old_space()->image_pages_;
   while (image_page != NULL) {
     ImagePageRange range = {image_page->object_start(),
                             image_page->object_end()};
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index e5c2426..9757a28 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -236,7 +236,7 @@
 
 HeapIterationScope::HeapIterationScope(Thread* thread, bool writable)
     : ThreadStackResource(thread),
-      heap_(isolate()->heap()),
+      heap_(isolate_group()->heap()),
       old_space_(heap_->old_space()),
       writable_(writable) {
   isolate()->safepoint_handler()->SafepointThreads(thread);
@@ -313,7 +313,7 @@
 }
 
 void HeapIterationScope::IterateVMIsolateObjects(ObjectVisitor* visitor) const {
-  Dart::vm_isolate()->heap()->VisitObjects(visitor);
+  Dart::vm_isolate_group()->heap()->VisitObjects(visitor);
 }
 
 void HeapIterationScope::IterateObjectPointers(
@@ -429,7 +429,7 @@
 
 void Heap::EvacuateNewSpace(Thread* thread, GCReason reason) {
   ASSERT((reason != kOldSpace) && (reason != kPromotion));
-  if (thread->isolate_group() == Dart::vm_isolate()->group()) {
+  if (thread->isolate_group() == Dart::vm_isolate_group()) {
     // The vm isolate cannot safely collect garbage due to unvisited read-only
     // handles and slots bootstrapped with RAW_NULL. Ignore GC requests to
     // trigger a nice out-of-memory message instead of a crash in the middle of
@@ -453,7 +453,7 @@
 void Heap::CollectNewSpaceGarbage(Thread* thread, GCReason reason) {
   NoActiveIsolateScope no_active_isolate_scope;
   ASSERT((reason != kOldSpace) && (reason != kPromotion));
-  if (thread->isolate_group() == Dart::vm_isolate()->group()) {
+  if (thread->isolate_group() == Dart::vm_isolate_group()) {
     // The vm isolate cannot safely collect garbage due to unvisited read-only
     // handles and slots bootstrapped with RAW_NULL. Ignore GC requests to
     // trigger a nice out-of-memory message instead of a crash in the middle of
@@ -493,7 +493,7 @@
   if (FLAG_use_compactor) {
     type = kMarkCompact;
   }
-  if (thread->isolate_group() == Dart::vm_isolate()->group()) {
+  if (thread->isolate_group() == Dart::vm_isolate_group()) {
     // The vm isolate cannot safely collect garbage due to unvisited read-only
     // handles and slots bootstrapped with RAW_NULL. Ignore GC requests to
     // trigger a nice out-of-memory message instead of a crash in the middle of
@@ -740,7 +740,7 @@
 
   this->AddRegionsToObjectSet(allocated_set);
   Isolate* vm_isolate = Dart::vm_isolate();
-  vm_isolate->heap()->AddRegionsToObjectSet(allocated_set);
+  vm_isolate->group()->heap()->AddRegionsToObjectSet(allocated_set);
 
   {
     VerifyObjectVisitor object_visitor(isolate_group(), allocated_set,
@@ -756,7 +756,7 @@
     // VM isolate heap is premarked.
     VerifyObjectVisitor vm_object_visitor(isolate_group(), allocated_set,
                                           kRequireMarked);
-    vm_isolate->heap()->VisitObjects(&vm_object_visitor);
+    vm_isolate->group()->heap()->VisitObjects(&vm_object_visitor);
   }
 
   return allocated_set;
@@ -1186,37 +1186,37 @@
 
 NoHeapGrowthControlScope::NoHeapGrowthControlScope()
     : ThreadStackResource(Thread::Current()) {
-  Heap* heap = isolate()->heap();
+  Heap* heap = isolate_group()->heap();
   current_growth_controller_state_ = heap->GrowthControlState();
   heap->DisableGrowthControl();
 }
 
 NoHeapGrowthControlScope::~NoHeapGrowthControlScope() {
-  Heap* heap = isolate()->heap();
+  Heap* heap = isolate_group()->heap();
   heap->SetGrowthControlState(current_growth_controller_state_);
 }
 
 WritableVMIsolateScope::WritableVMIsolateScope(Thread* thread)
     : ThreadStackResource(thread) {
   if (FLAG_write_protect_code && FLAG_write_protect_vm_isolate) {
-    Dart::vm_isolate()->heap()->WriteProtect(false);
+    Dart::vm_isolate_group()->heap()->WriteProtect(false);
   }
 }
 
 WritableVMIsolateScope::~WritableVMIsolateScope() {
-  ASSERT(Dart::vm_isolate()->heap()->UsedInWords(Heap::kNew) == 0);
+  ASSERT(Dart::vm_isolate_group()->heap()->UsedInWords(Heap::kNew) == 0);
   if (FLAG_write_protect_code && FLAG_write_protect_vm_isolate) {
-    Dart::vm_isolate()->heap()->WriteProtect(true);
+    Dart::vm_isolate_group()->heap()->WriteProtect(true);
   }
 }
 
 WritableCodePages::WritableCodePages(Thread* thread, Isolate* isolate)
     : StackResource(thread), isolate_(isolate) {
-  isolate_->heap()->WriteProtectCode(false);
+  isolate_->group()->heap()->WriteProtectCode(false);
 }
 
 WritableCodePages::~WritableCodePages() {
-  isolate_->heap()->WriteProtectCode(true);
+  isolate_->group()->heap()->WriteProtectCode(true);
 }
 
 }  // namespace dart
diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h
index f303a15..9fbe5c1 100644
--- a/runtime/vm/heap/heap.h
+++ b/runtime/vm/heap/heap.h
@@ -300,8 +300,9 @@
   void PrintMemoryUsageJSON(JSONObject* jsobj) const;
 
   // The heap map contains the sizes and class ids for the objects in each page.
-  void PrintHeapMapToJSONStream(Isolate* isolate, JSONStream* stream) {
-    old_space_.PrintHeapMapToJSONStream(isolate, stream);
+  void PrintHeapMapToJSONStream(IsolateGroup* isolate_group,
+                                JSONStream* stream) {
+    old_space_.PrintHeapMapToJSONStream(isolate_group, stream);
   }
 #endif  // PRODUCT
 
diff --git a/runtime/vm/heap/heap_test.cc b/runtime/vm/heap/heap_test.cc
index b2c1009..364840d 100644
--- a/runtime/vm/heap/heap_test.cc
+++ b/runtime/vm/heap/heap_test.cc
@@ -109,8 +109,8 @@
       "  return new A();\n"
       "}\n";
   Dart_Handle h_lib = TestCase::LoadTestScript(kScriptChars, NULL);
-  Isolate* isolate = Isolate::Current();
-  ClassTable* class_table = isolate->class_table();
+  auto isolate_group = IsolateGroup::Current();
+  ClassTable* class_table = isolate_group->class_table();
   {
     // GC before main so allocations during the tests don't cause unexpected GC.
     TransitionNativeToVM transition(thread);
@@ -135,7 +135,7 @@
       CountObjectsVisitor visitor(thread, class_table->NumCids());
       HeapIterationScope iter(thread);
       iter.IterateObjects(&visitor);
-      isolate->group()->VisitWeakPersistentHandles(&visitor);
+      isolate_group->VisitWeakPersistentHandles(&visitor);
       EXPECT_EQ(2, visitor.new_count_[cid]);
       EXPECT_EQ(0, visitor.old_count_[cid]);
     }
@@ -148,7 +148,7 @@
       CountObjectsVisitor visitor(thread, class_table->NumCids());
       HeapIterationScope iter(thread);
       iter.IterateObjects(&visitor);
-      isolate->group()->VisitWeakPersistentHandles(&visitor);
+      isolate_group->VisitWeakPersistentHandles(&visitor);
       EXPECT_EQ(1, visitor.new_count_[cid]);
       EXPECT_EQ(0, visitor.old_count_[cid]);
     }
@@ -162,7 +162,7 @@
       CountObjectsVisitor visitor(thread, class_table->NumCids());
       HeapIterationScope iter(thread);
       iter.IterateObjects(&visitor);
-      isolate->group()->VisitWeakPersistentHandles(&visitor);
+      isolate_group->VisitWeakPersistentHandles(&visitor);
       EXPECT_EQ(0, visitor.new_count_[cid]);
       EXPECT_EQ(1, visitor.old_count_[cid]);
     }
@@ -175,7 +175,7 @@
       CountObjectsVisitor visitor(thread, class_table->NumCids());
       HeapIterationScope iter(thread);
       iter.IterateObjects(&visitor);
-      isolate->group()->VisitWeakPersistentHandles(&visitor);
+      isolate_group->VisitWeakPersistentHandles(&visitor);
       EXPECT_EQ(0, visitor.new_count_[cid]);
       EXPECT_EQ(1, visitor.old_count_[cid]);
     }
@@ -187,7 +187,7 @@
       CountObjectsVisitor visitor(thread, class_table->NumCids());
       HeapIterationScope iter(thread);
       iter.IterateObjects(&visitor);
-      isolate->group()->VisitWeakPersistentHandles(&visitor);
+      isolate_group->VisitWeakPersistentHandles(&visitor);
       EXPECT_EQ(0, visitor.new_count_[cid]);
       EXPECT_EQ(1, visitor.old_count_[cid]);
     }
@@ -203,7 +203,7 @@
       CountObjectsVisitor visitor(thread, class_table->NumCids());
       HeapIterationScope iter(thread);
       iter.IterateObjects(&visitor);
-      isolate->group()->VisitWeakPersistentHandles(&visitor);
+      isolate_group->VisitWeakPersistentHandles(&visitor);
       EXPECT_EQ(0, visitor.new_count_[cid]);
       EXPECT_EQ(0, visitor.old_count_[cid]);
     }
@@ -234,8 +234,7 @@
 };
 
 ISOLATE_UNIT_TEST_CASE(FindObject) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
   Heap::Space spaces[2] = {Heap::kOld, Heap::kNew};
   for (size_t space = 0; space < ARRAY_SIZE(spaces); ++space) {
     const String& obj = String::Handle(String::New("x", spaces[space]));
@@ -261,7 +260,7 @@
   // progress.
   GCTestHelper::WaitForGCTasks();
 
-  Heap* heap = Thread::Current()->isolate()->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
   EXPECT(heap->Contains(ObjectLayout::ToAddr(obj.raw())));
   heap->WriteProtect(true);
   EXPECT(heap->Contains(ObjectLayout::ToAddr(obj.raw())));
@@ -270,8 +269,7 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_DeadOldToNew) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   heap->CollectAllGarbage();
   heap->WaitForMarkerTasks(thread);  // Finalize marking to get live size.
@@ -294,8 +292,7 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_DeadNewToOld) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   heap->CollectAllGarbage();
   heap->WaitForMarkerTasks(thread);  // Finalize marking to get live size.
@@ -318,8 +315,7 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_DeadGenCycle) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   heap->CollectAllGarbage();
   heap->WaitForMarkerTasks(thread);  // Finalize marking to get live size.
@@ -343,8 +339,7 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveNewToOld) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   heap->CollectAllGarbage();
   heap->WaitForMarkerTasks(thread);  // Finalize marking to get live size.
@@ -366,8 +361,7 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveOldToNew) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   heap->CollectAllGarbage();
   heap->WaitForMarkerTasks(thread);  // Finalize marking to get live size.
@@ -389,8 +383,7 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveOldDeadNew) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   heap->CollectAllGarbage();
   heap->WaitForMarkerTasks(thread);  // Finalize marking to get live size.
@@ -412,8 +405,7 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveNewDeadOld) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   heap->CollectAllGarbage();
   heap->WaitForMarkerTasks(thread);  // Finalize marking to get live size.
@@ -435,8 +427,7 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveNewToOldChain) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   heap->CollectAllGarbage();
   intptr_t size_before =
@@ -459,8 +450,7 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(CollectAllGarbage_LiveOldToNewChain) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
 
   heap->CollectAllGarbage();
   intptr_t size_before =
@@ -485,8 +475,8 @@
 static void NoopFinalizer(void* isolate_callback_data, void* peer) {}
 
 ISOLATE_UNIT_TEST_CASE(ExternalPromotion) {
-  Isolate* isolate = Isolate::Current();
-  Heap* heap = isolate->heap();
+  auto isolate_group = IsolateGroup::Current();
+  Heap* heap = isolate_group->heap();
 
   heap->CollectAllGarbage();
   intptr_t size_before = kWordSize * (heap->new_space()->ExternalInWords() +
@@ -496,7 +486,8 @@
   Array& neu = Array::Handle();
   for (intptr_t i = 0; i < 100; i++) {
     neu = Array::New(1, Heap::kNew);
-    FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB,
+    FinalizablePersistentHandle::New(isolate_group, neu, NULL, NoopFinalizer,
+                                     1 * MB,
                                      /*auto_delete=*/true);
     old.SetAt(i, neu);
   }
@@ -548,7 +539,8 @@
       Bequest* bequest = message->bequest();
       PersistentHandle* handle = bequest->handle();
       // Object in the receiving isolate's heap.
-      EXPECT(isolate()->heap()->Contains(ObjectLayout::ToAddr(handle->raw())));
+      EXPECT(isolate()->group()->heap()->Contains(
+          ObjectLayout::ToAddr(handle->raw())));
       response_obj = handle->raw();
       isolate()->group()->api_state()->FreePersistentHandle(handle);
     } else {
@@ -662,14 +654,15 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(ExternalAllocationStats) {
-  Isolate* isolate = thread->isolate();
-  Heap* heap = thread->heap();
+  auto isolate_group = thread->isolate_group();
+  Heap* heap = isolate_group->heap();
 
   Array& old = Array::Handle(Array::New(100, Heap::kOld));
   Array& neu = Array::Handle();
   for (intptr_t i = 0; i < 100; i++) {
     neu = Array::New(1, Heap::kNew);
-    FinalizablePersistentHandle::New(isolate, neu, NULL, NoopFinalizer, 1 * MB,
+    FinalizablePersistentHandle::New(isolate_group, neu, NULL, NoopFinalizer,
+                                     1 * MB,
                                      /*auto_delete=*/true);
     old.SetAt(i, neu);
 
@@ -679,10 +672,11 @@
       HeapTestHelper::Scavenge(thread);
     }
 
-    CountObjectsVisitor visitor(thread, isolate->class_table()->NumCids());
+    CountObjectsVisitor visitor(thread,
+                                isolate_group->class_table()->NumCids());
     HeapIterationScope iter(thread);
     iter.IterateObjects(&visitor);
-    isolate->group()->VisitWeakPersistentHandles(&visitor);
+    isolate_group->VisitWeakPersistentHandles(&visitor);
     EXPECT_LE(visitor.old_external_size_[kArrayCid],
               heap->old_space()->ExternalInWords() * kWordSize);
     EXPECT_LE(visitor.new_external_size_[kArrayCid],
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index 39180e8..12d4165 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -895,7 +895,7 @@
   JSONArray* array_;
 };
 
-void PageSpace::PrintHeapMapToJSONStream(Isolate* isolate,
+void PageSpace::PrintHeapMapToJSONStream(IsolateGroup* isolate_group,
                                          JSONStream* stream) const {
   JSONObject heap_map(stream);
   heap_map.AddProperty("type", "HeapMap");
@@ -905,7 +905,7 @@
   heap_map.AddProperty("pageSizeBytes", kOldPageSizeInWords * kWordSize);
   {
     JSONObject class_list(&heap_map, "classList");
-    isolate->class_table()->PrintToJSONObject(&class_list);
+    isolate_group->class_table()->PrintToJSONObject(&class_list);
   }
   {
     // "pages" is an array [page0, page1, ..., pageN], each page of the form
diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h
index 434ad7f..48b77ca 100644
--- a/runtime/vm/heap/pages.h
+++ b/runtime/vm/heap/pages.h
@@ -410,7 +410,8 @@
 
 #ifndef PRODUCT
   void PrintToJSONObject(JSONObject* object) const;
-  void PrintHeapMapToJSONStream(Isolate* isolate, JSONStream* stream) const;
+  void PrintHeapMapToJSONStream(IsolateGroup* isolate_group,
+                                JSONStream* stream) const;
 #endif  // PRODUCT
 
   void AllocateBlack(intptr_t size) {
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index f22ab54..2920df3 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -1383,7 +1383,7 @@
 }
 
 void Scavenger::TryAllocateNewTLAB(Thread* thread, intptr_t min_size) {
-  ASSERT(heap_ != Dart::vm_isolate()->heap());
+  ASSERT(heap_ != Dart::vm_isolate_group()->heap());
   ASSERT(!scavenging_);
 
   AbandonRemainingTLAB(thread);
diff --git a/runtime/vm/heap/scavenger.h b/runtime/vm/heap/scavenger.h
index 785825e..1c52430 100644
--- a/runtime/vm/heap/scavenger.h
+++ b/runtime/vm/heap/scavenger.h
@@ -373,7 +373,7 @@
 
   uword TryAllocateFromTLAB(Thread* thread, intptr_t size) {
     ASSERT(Utils::IsAligned(size, kObjectAlignment));
-    ASSERT(heap_ != Dart::vm_isolate()->heap());
+    ASSERT(heap_ != Dart::vm_isolate_group()->heap());
 
     const uword result = thread->top();
     const intptr_t remaining = thread->end() - result;
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index 8b58acb..02c2c18 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -435,7 +435,7 @@
 void ImageWriter::Write(NonStreamingWriteStream* clustered_stream, bool vm) {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Heap* heap = thread->isolate()->heap();
+  Heap* heap = thread->isolate_group()->heap();
   TIMELINE_DURATION(thread, Isolate, "WriteInstructions");
 
   // Handlify collected raw pointers as building the names below
diff --git a/runtime/vm/image_snapshot.h b/runtime/vm/image_snapshot.h
index cc5ac6d..87b7a5a 100644
--- a/runtime/vm/image_snapshot.h
+++ b/runtime/vm/image_snapshot.h
@@ -501,7 +501,7 @@
         owner_(Object::Handle(zone)),
         string_(String::Handle(zone)),
         insns_(Instructions::Handle(zone)),
-        store_(Isolate::Current()->object_store()) {}
+        store_(IsolateGroup::Current()->object_store()) {}
 
   const char* StubNameForType(const AbstractType& type) const;
 
diff --git a/runtime/vm/instructions_arm.cc b/runtime/vm/instructions_arm.cc
index bfeaf1c..d38a349 100644
--- a/runtime/vm/instructions_arm.cc
+++ b/runtime/vm/instructions_arm.cc
@@ -319,7 +319,7 @@
   if (result != Code::null()) {
     return result;
   }
-  result = ReversePc::Lookup(Dart::vm_isolate()->group(), pc);
+  result = ReversePc::Lookup(Dart::vm_isolate_group(), pc);
   if (result != Code::null()) {
     return result;
   }
diff --git a/runtime/vm/instructions_arm64.cc b/runtime/vm/instructions_arm64.cc
index 632e69a..80ee77a 100644
--- a/runtime/vm/instructions_arm64.cc
+++ b/runtime/vm/instructions_arm64.cc
@@ -461,7 +461,7 @@
   if (result != Code::null()) {
     return result;
   }
-  result = ReversePc::Lookup(Dart::vm_isolate()->group(), pc);
+  result = ReversePc::Lookup(Dart::vm_isolate_group(), pc);
   if (result != Code::null()) {
     return result;
   }
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 8973f04..e026393 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -106,6 +106,7 @@
 // Quick access to the locally defined thread() and isolate() methods.
 #define T (thread())
 #define I (isolate())
+#define IG (isolate_group())
 
 #if defined(DEBUG)
 // Helper class to ensure that a live origin_id is never reused
@@ -883,15 +884,15 @@
   }
 #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
   if (cls.IsTopLevel()) {
-    class_table()->RegisterTopLevel(cls);
+    group()->class_table()->RegisterTopLevel(cls);
   } else {
-    class_table()->Register(cls);
+    group()->class_table()->Register(cls);
   }
 }
 
 #if defined(DEBUG)
 void Isolate::ValidateClassTable() {
-  class_table()->Validate();
+  group()->class_table()->Validate();
 }
 #endif  // DEBUG
 
@@ -935,10 +936,10 @@
   thread->heap()->ResetCanonicalHashTable();
 
   Class& cls = Class::Handle(zone);
-  intptr_t top = class_table()->NumCids();
+  intptr_t top = group()->class_table()->NumCids();
   for (intptr_t cid = kInstanceCid; cid < top; cid++) {
-    if (!class_table()->IsValidIndex(cid) ||
-        !class_table()->HasValidClassAt(cid)) {
+    if (!group()->class_table()->IsValidIndex(cid) ||
+        !group()->class_table()->HasValidClassAt(cid)) {
       continue;
     }
     if ((cid == kTypeArgumentsCid) || IsStringClassId(cid)) {
@@ -946,7 +947,7 @@
       // that aren't based on address.
       continue;
     }
-    cls = class_table()->At(cid);
+    cls = group()->class_table()->At(cid);
     cls.RehashConstants(zone);
   }
 }
@@ -963,7 +964,7 @@
   // Verify that all canonical instances are correctly setup in the
   // corresponding canonical tables.
   BackgroundCompiler::Stop(this);
-  heap()->CollectAllGarbage();
+  group()->heap()->CollectAllGarbage();
   Thread* thread = Thread::Current();
   HeapIterationScope iteration(thread);
   VerifyCanonicalVisitor check_canonical(thread);
@@ -1013,6 +1014,7 @@
 #endif
   bool IsCurrentIsolate() const;
   virtual Isolate* isolate() const { return isolate_; }
+  virtual IsolateGroup* isolate_group() const { return isolate_->group(); }
 
  private:
   // A result of false indicates that the isolate should terminate the
@@ -1154,7 +1156,7 @@
       break;
     }
     case Isolate::kLowMemoryMsg: {
-      I->heap()->NotifyLowMemory();
+      I->group()->heap()->NotifyLowMemory();
       break;
     }
     case Isolate::kDrainServiceExtensionsMsg: {
@@ -1479,9 +1481,9 @@
     Zone* zone = T->zone();
     const UnhandledException& uhe = UnhandledException::Cast(result);
     const Instance& exception = Instance::Handle(zone, uhe.exception());
-    if (exception.raw() == I->object_store()->out_of_memory()) {
+    if (exception.raw() == IG->object_store()->out_of_memory()) {
       exception_cstr = "Out of Memory";  // Cf. OutOfMemoryError.toString().
-    } else if (exception.raw() == I->object_store()->stack_overflow()) {
+    } else if (exception.raw() == IG->object_store()->stack_overflow()) {
       exception_cstr = "Stack Overflow";  // Cf. StackOverflowError.toString().
     } else {
       const Object& exception_str =
@@ -1519,8 +1521,8 @@
       if (result.IsUnhandledException()) {
         const UnhandledException& error = UnhandledException::Cast(result);
         InstancePtr exception = error.exception();
-        if ((exception == I->object_store()->out_of_memory()) ||
-            (exception == I->object_store()->stack_overflow())) {
+        if ((exception == IG->object_store()->out_of_memory()) ||
+            (exception == IG->object_store()->stack_overflow())) {
           // We didn't notify the debugger when the stack was full. Do it now.
           I->debugger()->PauseException(Instance::Handle(exception));
         }
@@ -1780,7 +1782,7 @@
     // Non-vm isolates need to have isolate object store initialized is that
     // exit_listeners have to be null-initialized as they will be used if
     // we fail to create isolate below, have to do low level shutdown.
-    ASSERT(result->object_store() != nullptr);
+    ASSERT(result->group()->object_store() != nullptr);
     result->isolate_object_store()->Init();
   }
 
@@ -1892,8 +1894,8 @@
 
 void Isolate::SetupImagePage(const uint8_t* image_buffer, bool is_executable) {
   Image image(image_buffer);
-  heap()->SetupImagePage(image.object_start(), image.object_size(),
-                         is_executable);
+  group()->heap()->SetupImagePage(image.object_start(), image.object_size(),
+                                  is_executable);
 }
 
 void Isolate::ScheduleInterrupts(uword interrupt_bits) {
@@ -2060,7 +2062,7 @@
   if (is_runnable() == true) {
     return "Isolate is already runnable";
   }
-  if (object_store()->root_library() == Library::null()) {
+  if (group()->object_store()->root_library() == Library::null()) {
     return "The embedder has to ensure there is a root library (e.g. by "
            "calling Dart_LoadScriptFromKernel ).";
   }
@@ -2071,7 +2073,7 @@
 void Isolate::MakeRunnableLocked() {
   ASSERT(mutex_.IsOwnedByCurrentThread());
   ASSERT(!is_runnable());
-  ASSERT(object_store()->root_library() != Library::null());
+  ASSERT(group()->object_store()->root_library() != Library::null());
 
   // Set the isolate as runnable and if we are being spawned schedule
   // isolate on thread pool for execution.
@@ -2329,7 +2331,7 @@
 void Isolate::AddClosureFunction(const Function& function) const {
   ASSERT(!Compiler::IsBackgroundCompilation());
   GrowableObjectArray& closures =
-      GrowableObjectArray::Handle(object_store()->closure_functions());
+      GrowableObjectArray::Handle(group()->object_store()->closure_functions());
   ASSERT(!closures.IsNull());
   ASSERT(function.IsNonImplicitClosureFunction());
   closures.Add(function, Heap::kOld);
@@ -2344,7 +2346,7 @@
 FunctionPtr Isolate::LookupClosureFunction(const Function& parent,
                                            TokenPosition token_pos) const {
   const GrowableObjectArray& closures =
-      GrowableObjectArray::Handle(object_store()->closure_functions());
+      GrowableObjectArray::Handle(group()->object_store()->closure_functions());
   ASSERT(!closures.IsNull());
   Function& closure = Function::Handle();
   intptr_t num_closures = closures.Length();
@@ -2360,7 +2362,7 @@
 
 intptr_t Isolate::FindClosureIndex(const Function& needle) const {
   const GrowableObjectArray& closures_array =
-      GrowableObjectArray::Handle(object_store()->closure_functions());
+      GrowableObjectArray::Handle(group()->object_store()->closure_functions());
   intptr_t num_closures = closures_array.Length();
   for (intptr_t i = 0; i < num_closures; i++) {
     if (closures_array.At(i) == needle.raw()) {
@@ -2372,7 +2374,7 @@
 
 FunctionPtr Isolate::ClosureFunctionFromIndex(intptr_t idx) const {
   const GrowableObjectArray& closures_array =
-      GrowableObjectArray::Handle(object_store()->closure_functions());
+      GrowableObjectArray::Handle(group()->object_store()->closure_functions());
   if ((idx < 0) || (idx >= closures_array.Length())) {
     return Function::null();
   }
@@ -2393,7 +2395,7 @@
   NoSafepointScope no_safepoint_scope;
 
   // Notify exit listeners that this isolate is shutting down.
-  if (object_store() != nullptr) {
+  if (group()->object_store() != nullptr) {
     const Error& error = Error::Handle(thread->sticky_error());
     if (error.IsNull() || !error.IsUnwindError() ||
         UnwindError::Cast(error).is_user_initiated()) {
@@ -2426,10 +2428,10 @@
     MegamorphicCacheTable::PrintSizes(this);
   }
   if (FLAG_dump_symbol_stats) {
-    Symbols::DumpStats(this);
+    Symbols::DumpStats(group());
   }
   if (FLAG_trace_isolates) {
-    heap()->PrintSizes();
+    group()->heap()->PrintSizes();
     OS::PrintErr(
         "[-] Stopping isolate:\n"
         "\tisolate:    %s\n",
@@ -2610,21 +2612,11 @@
                                   ValidationPolicy validate_frames) {
   ASSERT(visitor != nullptr);
 
-  // Visit objects in the object store if there is no isolate group object store
-  if (group()->object_store() == nullptr && object_store() != nullptr) {
-    object_store()->VisitObjectPointers(visitor);
-  }
   // Visit objects in the isolate object store.
   if (isolate_object_store() != nullptr) {
     isolate_object_store()->VisitObjectPointers(visitor);
   }
 
-  // Visit objects in the class table unless it's shared by the group.
-  // If it is shared, it is visited by IsolateGroup::VisitObjectPointers
-  if (group()->class_table() != class_table()) {
-    class_table()->VisitObjectPointers(visitor);
-  }
-
   // Visit objects in the field table.
   if (!visitor->trace_values_through_fields()) {
     field_table()->VisitObjectPointers(visitor);
@@ -2871,10 +2863,10 @@
   if (group()->IsReloading()) {
     raw_class = reload_context()->GetClassForHeapWalkAt(cid);
   } else {
-    raw_class = class_table()->At(cid);
+    raw_class = group()->class_table()->At(cid);
   }
 #else
-  raw_class = class_table()->At(cid);
+  raw_class = group()->class_table()->At(cid);
 #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
   ASSERT(raw_class != nullptr);
   ASSERT(remapping_cids() || raw_class->ptr()->id_ == cid);
@@ -2972,8 +2964,8 @@
   jsobj.AddPropertyTimeMillis("startTime", start_time);
   {
     JSONObject jsheap(&jsobj, "_heaps");
-    heap()->PrintToJSONObject(Heap::kNew, &jsheap);
-    heap()->PrintToJSONObject(Heap::kOld, &jsheap);
+    group()->heap()->PrintToJSONObject(Heap::kNew, &jsheap);
+    group()->heap()->PrintToJSONObject(Heap::kOld, &jsheap);
   }
 
   {
@@ -3044,7 +3036,7 @@
     jsobj.AddProperty("pauseEvent", &pause_event);
   }
 
-  const Library& lib = Library::Handle(object_store()->root_library());
+  const Library& lib = Library::Handle(group()->object_store()->root_library());
   if (!lib.IsNull()) {
     jsobj.AddProperty("rootLib", lib);
   }
@@ -3065,7 +3057,7 @@
 
   {
     const GrowableObjectArray& libs =
-        GrowableObjectArray::Handle(object_store()->libraries());
+        GrowableObjectArray::Handle(group()->object_store()->libraries());
     intptr_t num_libs = libs.Length();
     Library& lib = Library::Handle();
 
@@ -3115,7 +3107,7 @@
 }
 
 void Isolate::PrintMemoryUsageJSON(JSONStream* stream) {
-  heap()->PrintMemoryUsageJSON(stream);
+  group()->heap()->PrintMemoryUsageJSON(stream);
 }
 
 #endif
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index c24cfd6..1214308 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -183,13 +183,13 @@
 //     V(when, name, bit-name, Dart_IsolateFlags-name, command-line-flag-name)
 //
 #define BOOL_ISOLATE_GROUP_FLAG_LIST_DEFAULT_GETTER(V)                         \
-  V(PRECOMPILER, obfuscate, Obfuscate, obfuscate, false)
-
-#define BOOL_ISOLATE_FLAG_LIST_DEFAULT_GETTER(V)                               \
+  V(PRECOMPILER, obfuscate, Obfuscate, obfuscate, false)                       \
   V(NONPRODUCT, asserts, EnableAsserts, enable_asserts, FLAG_enable_asserts)   \
   V(NONPRODUCT, use_field_guards, UseFieldGuards, use_field_guards,            \
     FLAG_use_field_guards)                                                     \
-  V(NONPRODUCT, use_osr, UseOsr, use_osr, FLAG_use_osr)                        \
+  V(NONPRODUCT, use_osr, UseOsr, use_osr, FLAG_use_osr)
+
+#define BOOL_ISOLATE_FLAG_LIST_DEFAULT_GETTER(V)                               \
   V(PRODUCT, should_load_vmservice_library, ShouldLoadVmService,               \
     load_vmservice_library, false)                                             \
   V(PRODUCT, copy_parent_code, CopyParentCode, copy_parent_code, false)        \
@@ -472,6 +472,14 @@
     return null_safety() || FLAG_strict_null_safety_checks;
   }
 
+#if defined(PRODUCT)
+  void set_use_osr(bool use_osr) { ASSERT(!use_osr); }
+#else   // defined(PRODUCT)
+  void set_use_osr(bool use_osr) {
+    isolate_group_flags_ = UseOsrBit::update(use_osr, isolate_group_flags_);
+  }
+#endif  // defined(PRODUCT)
+
   StoreBuffer* store_buffer() const { return store_buffer_.get(); }
   ClassTable* class_table() const { return class_table_.get(); }
   ObjectStore* object_store() const { return object_store_.get(); }
@@ -709,9 +717,12 @@
 
 #define ISOLATE_GROUP_FLAG_BITS(V)                                             \
   V(CompactionInProgress)                                                      \
+  V(EnableAsserts)                                                             \
   V(NullSafety)                                                                \
   V(NullSafetySet)                                                             \
-  V(Obfuscate)
+  V(Obfuscate)                                                                 \
+  V(UseFieldGuards)                                                            \
+  V(UseOsr)
 
   // Isolate group specific flags.
   enum FlagBits {
@@ -895,8 +906,6 @@
     return group()->safepoint_handler();
   }
 
-  ClassTable* class_table() { return class_table_.get(); }
-
   ClassPtr* cached_class_table_table() { return cached_class_table_table_; }
   void set_cached_class_table_table(ClassPtr* cached_class_table_table) {
     cached_class_table_table_ = cached_class_table_table;
@@ -905,7 +914,6 @@
     return OFFSET_OF(Isolate, cached_class_table_table_);
   }
 
-  SharedClassTable* shared_class_table() const { return shared_class_table_; }
   // Used during isolate creation to re-register isolate with right group.
   void set_shared_class_table(SharedClassTable* table) {
     shared_class_table_ = table;
@@ -915,7 +923,6 @@
     return OFFSET_OF(Isolate, shared_class_table_);
   }
 
-  ObjectStore* object_store() const { return object_store_shared_ptr_.get(); }
   void set_object_store(ObjectStore* object_store);
   static intptr_t cached_object_store_offset() {
     return OFFSET_OF(Isolate, cached_object_store_);
@@ -992,8 +999,6 @@
 
   void SendInternalLibMessage(LibMsgId msg_id, uint64_t capability);
 
-  Heap* heap() const { return isolate_group_->heap(); }
-
   void set_init_callback_data(void* value) { init_callback_data_ = value; }
   void* init_callback_data() const { return init_callback_data_; }
 
@@ -1379,14 +1384,6 @@
 #undef FLAG_FOR_PRODUCT
 #undef DECLARE_GETTER
 
-#if defined(PRODUCT)
-  void set_use_osr(bool use_osr) { ASSERT(!use_osr); }
-#else   // defined(PRODUCT)
-  void set_use_osr(bool use_osr) {
-    isolate_flags_ = UseOsrBit::update(use_osr, isolate_flags_);
-  }
-#endif  // defined(PRODUCT)
-
   bool has_attempted_stepping() const {
     return HasAttemptedSteppingBit::decode(isolate_flags_);
   }
@@ -1558,9 +1555,6 @@
   V(HasAttemptedReload)                                                        \
   V(HasAttemptedStepping)                                                      \
   V(ShouldPausePostServiceRequest)                                             \
-  V(EnableAsserts)                                                             \
-  V(UseFieldGuards)                                                            \
-  V(UseOsr)                                                                    \
   V(CopyParentCode)                                                            \
   V(ShouldLoadVmService)                                                       \
   V(IsSystemIsolate)
diff --git a/runtime/vm/isolate_reload.cc b/runtime/vm/isolate_reload.cc
index d0898d3..82db8b2 100644
--- a/runtime/vm/isolate_reload.cc
+++ b/runtime/vm/isolate_reload.cc
@@ -60,6 +60,7 @@
 DECLARE_FLAG(bool, trace_deoptimization);
 
 #define I (isolate())
+#define IG (isolate_group())
 #define Z zone_
 
 #define TIMELINE_SCOPE(name)                                                   \
@@ -559,21 +560,14 @@
 
   Thread* thread = Thread::Current();
 
-  // All isolates have the same sources, so all of them have the same libraries.
-  // We use the [first_isolate_] here to determine which of libraries have
-  // changed.
-  ASSERT(first_isolate_ == nullptr);
-  first_isolate_ = thread->isolate();
-
   // All isolates within an isolate group need to share one heap.
   // TODO(dartbug.com/36097): Remove this assert once the shared heap CL has
   // landed.
   RELEASE_ASSERT(!FLAG_enable_isolate_groups);
-  Heap* heap = first_isolate_->heap();
+  Heap* heap = IG->heap();
 
-  num_old_libs_ = GrowableObjectArray::Handle(
-                      Z, first_isolate_->object_store()->libraries())
-                      .Length();
+  num_old_libs_ =
+      GrowableObjectArray::Handle(Z, IG->object_store()->libraries()).Length();
 
   // Grab root library before calling CheckpointBeforeReload.
   GetRootLibUrl(root_script_url);
@@ -632,7 +626,7 @@
 
     modified_libs_ = new (Z) BitVector(Z, num_old_libs_);
     kernel::KernelLoader::FindModifiedLibraries(
-        kernel_program.get(), first_isolate_, modified_libs_, force_reload,
+        kernel_program.get(), isolate_group_, modified_libs_, force_reload,
         &skip_reload, p_num_received_classes, p_num_received_procedures);
     modified_libs_transitive_ = new (Z) BitVector(Z, num_old_libs_);
     BuildModifiedLibrariesClosure(modified_libs_);
@@ -859,15 +853,13 @@
     // not remove dead subclasses.  Rebuild the direct subclass
     // information from scratch.
     {
-      SafepointWriteRwLocker ml(thread,
-                                thread->isolate_group()->program_lock());
+      SafepointWriteRwLocker ml(thread, IG->program_lock());
       ForEachIsolate([&](Isolate* isolate) {
         isolate->reload_context()->RebuildDirectSubclasses();
       });
     }
     const intptr_t final_library_count =
-        GrowableObjectArray::Handle(Z,
-                                    first_isolate_->object_store()->libraries())
+        GrowableObjectArray::Handle(Z, IG->object_store()->libraries())
             .Length();
     CommonFinalizeTail(final_library_count);
 
@@ -932,7 +924,7 @@
 void IsolateGroupReloadContext::BuildModifiedLibrariesClosure(
     BitVector* modified_libs) {
   const GrowableObjectArray& libs =
-      GrowableObjectArray::Handle(first_isolate_->object_store()->libraries());
+      GrowableObjectArray::Handle(IG->object_store()->libraries());
   Library& lib = Library::Handle();
   intptr_t num_libs = libs.Length();
 
@@ -1017,7 +1009,7 @@
 
 void IsolateGroupReloadContext::GetRootLibUrl(const char* root_script_url) {
   const auto& old_root_lib =
-      Library::Handle(first_isolate_->object_store()->root_library());
+      Library::Handle(IG->object_store()->root_library());
   ASSERT(!old_root_lib.IsNull());
   const auto& old_root_lib_url = String::Handle(old_root_lib.url());
 
@@ -1114,7 +1106,7 @@
     if (lib.IsNull()) {
       lib = Library::LookupLibrary(thread, root_lib_url);
     }
-    isolate_->object_store()->set_root_library(lib);
+    isolate_->group()->object_store()->set_root_library(lib);
     return Object::null();
   } else {
     return thread->StealStickyError();
@@ -1145,9 +1137,9 @@
   const Class& old_cls = Class::Handle(OldClassOrNull(new_cls));
   if (old_cls.IsNull()) {
     if (new_cls.IsTopLevel()) {
-      I->class_table()->RegisterTopLevel(new_cls);
+      IG->class_table()->RegisterTopLevel(new_cls);
     } else {
-      I->class_table()->Register(new_cls);
+      IG->class_table()->Register(new_cls);
     }
 
     if (FLAG_identity_reload) {
@@ -1162,7 +1154,7 @@
   }
   VTIR_Print("Registering class: %s\n", new_cls.ToCString());
   new_cls.set_id(old_cls.id());
-  I->class_table()->SetAt(old_cls.id(), new_cls.raw());
+  IG->class_table()->SetAt(old_cls.id(), new_cls.raw());
   if (!old_cls.is_enum_class()) {
     new_cls.CopyCanonicalConstants(old_cls);
   }
@@ -1241,15 +1233,15 @@
 
 void IsolateReloadContext::DeoptimizeDependentCode() {
   TIMELINE_SCOPE(DeoptimizeDependentCode);
-  ClassTable* class_table = I->class_table();
+  ClassTable* class_table = IG->class_table();
 
-  const intptr_t bottom = Dart::vm_isolate()->class_table()->NumCids();
-  const intptr_t top = I->class_table()->NumCids();
+  const intptr_t bottom = Dart::vm_isolate_group()->class_table()->NumCids();
+  const intptr_t top = IG->class_table()->NumCids();
   Class& cls = Class::Handle();
   Array& fields = Array::Handle();
   Field& field = Field::Handle();
   Thread* thread = Thread::Current();
-  SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+  SafepointWriteRwLocker ml(thread, IG->program_lock());
   for (intptr_t cls_idx = bottom; cls_idx < top; cls_idx++) {
     if (!class_table->HasValidClassAt(cls_idx)) {
       // Skip.
@@ -1308,7 +1300,7 @@
   // is used to pair new classes with old classes.
 
   // Copy the class table for isolate.
-  ClassTable* class_table = I->class_table();
+  ClassTable* class_table = IG->class_table();
   ClassPtr* saved_class_table = nullptr;
   ClassPtr* saved_tlc_class_table = nullptr;
   class_table->CopyBeforeHotReload(&saved_class_table, &saved_tlc_class_table,
@@ -1392,7 +1384,7 @@
   const int64_t last_reload = isolate_group_->last_reload_timestamp();
   GrowableArray<const char*> modified_sources_uris;
   const auto& libs =
-      GrowableObjectArray::Handle(first_isolate_->object_store()->libraries());
+      GrowableObjectArray::Handle(IG->object_store()->libraries());
   Library& lib = Library::Handle(Z);
   Array& scripts = Array::Handle(Z);
   Script& script = Script::Handle(Z);
@@ -1652,7 +1644,7 @@
     // Update the libraries array.
     Library& lib = Library::Handle();
     const GrowableObjectArray& libs =
-        GrowableObjectArray::Handle(I->object_store()->libraries());
+        GrowableObjectArray::Handle(IG->object_store()->libraries());
     for (intptr_t i = 0; i < libs.Length(); i++) {
       lib = Library::RawCast(libs.At(i));
       VTIR_Print("Lib '%s' at index %" Pd "\n", lib.ToCString(), i);
@@ -1719,17 +1711,17 @@
 #endif
 
   if (FLAG_identity_reload) {
-    if (saved_num_cids_ != I->class_table()->NumCids()) {
+    if (saved_num_cids_ != IG->class_table()->NumCids()) {
       TIR_Print("Identity reload failed! B#C=%" Pd " A#C=%" Pd "\n",
-                saved_num_cids_, I->class_table()->NumCids());
+                saved_num_cids_, IG->class_table()->NumCids());
     }
-    if (saved_num_tlc_cids_ != I->class_table()->NumTopLevelCids()) {
+    if (saved_num_tlc_cids_ != IG->class_table()->NumTopLevelCids()) {
       TIR_Print("Identity reload failed! B#TLC=%" Pd " A#TLC=%" Pd "\n",
-                saved_num_tlc_cids_, I->class_table()->NumTopLevelCids());
+                saved_num_tlc_cids_, IG->class_table()->NumTopLevelCids());
     }
     const auto& saved_libs = GrowableObjectArray::Handle(saved_libraries_);
     const GrowableObjectArray& libs =
-        GrowableObjectArray::Handle(I->object_store()->libraries());
+        GrowableObjectArray::Handle(IG->object_store()->libraries());
     if (saved_libs.Length() != libs.Length()) {
       TIR_Print("Identity reload failed! B#L=%" Pd " A#L=%" Pd "\n",
                 saved_libs.Length(), libs.Length());
@@ -1885,7 +1877,7 @@
   if (class_table != nullptr) {
     return class_table[index];
   }
-  return isolate_->class_table()->At(cid);
+  return IG->class_table()->At(cid);
 }
 
 intptr_t IsolateGroupReloadContext::GetClassSizeForHeapWalkAt(classid_t cid) {
@@ -1906,7 +1898,7 @@
       saved_class_table_.load(std::memory_order_relaxed);
   ClassPtr* local_saved_tlc_class_table =
       saved_tlc_class_table_.load(std::memory_order_relaxed);
-  I->class_table()->ResetAfterHotReload(
+  IG->class_table()->ResetAfterHotReload(
       local_saved_class_table, local_saved_tlc_class_table, saved_num_cids_,
       saved_num_tlc_cids_, is_rollback);
   saved_class_table_.store(nullptr, std::memory_order_release);
@@ -1944,7 +1936,7 @@
 }
 
 ObjectStore* IsolateReloadContext::object_store() {
-  return isolate_->object_store();
+  return isolate_->group()->object_store();
 }
 
 void IsolateReloadContext::ResetUnoptimizedICsOnStack() {
@@ -2333,7 +2325,7 @@
     const GrowableArray<const Field*>& fields,
     const GrowableArray<const Instance*>& instances) {
   TIMELINE_SCOPE(InvalidateFields);
-  SafepointMutexLocker ml(isolate()->group()->subtype_test_cache_mutex());
+  SafepointMutexLocker ml(IG->subtype_test_cache_mutex());
   FieldInvalidator invalidator(zone);
   invalidator.CheckStatics(fields);
   invalidator.CheckInstances(instances);
@@ -2584,7 +2576,7 @@
 }
 
 void IsolateReloadContext::RebuildDirectSubclasses() {
-  ClassTable* class_table = I->class_table();
+  ClassTable* class_table = IG->class_table();
   intptr_t num_cids = class_table->NumCids();
 
   // Clear the direct subclasses for all classes.
diff --git a/runtime/vm/isolate_reload.h b/runtime/vm/isolate_reload.h
index ad29eb0..6e6cd74 100644
--- a/runtime/vm/isolate_reload.h
+++ b/runtime/vm/isolate_reload.h
@@ -235,7 +235,6 @@
 
   int64_t start_time_micros_ = -1;
   int64_t reload_timestamp_ = -1;
-  Isolate* first_isolate_ = nullptr;
   bool reload_skipped_ = false;
   bool reload_finalized_ = false;
   JSONStream* js_;
@@ -335,6 +334,7 @@
   void VisitObjectPointers(ObjectPointerVisitor* visitor);
 
   Isolate* isolate() { return isolate_; }
+  IsolateGroup* isolate_group() { return isolate_->group(); }
   ObjectStore* object_store();
 
   void EnsuredUnoptimizedCodeForStack();
diff --git a/runtime/vm/isolate_reload_test.cc b/runtime/vm/isolate_reload_test.cc
index 6050dd30..fef7059 100644
--- a/runtime/vm/isolate_reload_test.cc
+++ b/runtime/vm/isolate_reload_test.cc
@@ -2357,7 +2357,7 @@
   String& name = String::Handle();
 
   // Lookup the Stopwatch class by name from the dart core library.
-  ObjectStore* object_store = Isolate::Current()->object_store();
+  ObjectStore* object_store = IsolateGroup::Current()->object_store();
   const Library& core_lib = Library::Handle(object_store->core_library());
   name = String::New("Stopwatch");
   const Class& stopwatch_cls = Class::Handle(core_lib.LookupClass(name));
@@ -2424,7 +2424,7 @@
   String& name = String::Handle();
 
   // Lookup the Stopwatch class by name from the dart core library.
-  ObjectStore* object_store = Isolate::Current()->object_store();
+  ObjectStore* object_store = IsolateGroup::Current()->object_store();
   const Library& core_lib = Library::Handle(object_store->core_library());
   name = String::New("Stopwatch");
   const Class& stopwatch_cls = Class::Handle(core_lib.LookupClass(name));
@@ -2493,7 +2493,7 @@
   String& name = String::Handle();
 
   // Lookup the Stopwatch class by name from the dart core library.
-  ObjectStore* object_store = Isolate::Current()->object_store();
+  ObjectStore* object_store = IsolateGroup::Current()->object_store();
   const Library& core_lib = Library::Handle(object_store->core_library());
   name = String::New("Stopwatch");
   const Class& stopwatch_cls = Class::Handle(core_lib.LookupClass(name));
@@ -4195,8 +4195,7 @@
       HeapIterationScope iteration(thread);
       NoSafepointScope no_safepoint;
       FindNoInstancesOfClass find_only(cid);
-      Isolate* isolate = Isolate::Current();
-      Heap* heap = isolate->heap();
+      Heap* heap = IsolateGroup::Current()->heap();
       // We still expect to find references to static field values
       // because they are not deleted after hot reload.
       EXPECT_NE(heap->FindObject(&find_only), Object::null());
diff --git a/runtime/vm/isolate_test.cc b/runtime/vm/isolate_test.cc
index 10a09c9..9402106 100644
--- a/runtime/vm/isolate_test.cc
+++ b/runtime/vm/isolate_test.cc
@@ -125,10 +125,9 @@
 // compiler and/or CPU could reorder operations to make the tasks observe the
 // round update *before* the interrupt is set.
 TEST_CASE(StackLimitInterrupts) {
-  Isolate* isolate = thread->isolate();
-  ThreadBarrier barrier(InterruptChecker::kTaskCount + 1,
-                        isolate->heap()->barrier(),
-                        isolate->heap()->barrier_done());
+  auto heap = thread->isolate_group()->heap();
+  ThreadBarrier barrier(InterruptChecker::kTaskCount + 1, heap->barrier(),
+                        heap->barrier_done());
   // Start all tasks. They will busy-wait until interrupted in the first round.
   for (intptr_t task = 0; task < InterruptChecker::kTaskCount; task++) {
     Dart::thread_pool()->Run<InterruptChecker>(thread, &barrier);
diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc
index 144b228..59f66bf 100644
--- a/runtime/vm/kernel.cc
+++ b/runtime/vm/kernel.cc
@@ -244,9 +244,9 @@
 
   GrowableArray<intptr_t> token_positions(10);
 
-  Isolate* isolate = thread->isolate();
-  const GrowableObjectArray& libs =
-      GrowableObjectArray::Handle(zone, isolate->object_store()->libraries());
+  auto isolate_group = thread->isolate_group();
+  const GrowableObjectArray& libs = GrowableObjectArray::Handle(
+      zone, isolate_group->object_store()->libraries());
   Library& lib = Library::Handle(zone);
   Object& entry = Object::Handle(zone);
   Script& entry_script = Script::Handle(zone);
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index d3f300e..9ab6116 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -179,7 +179,7 @@
     HANDLESCOPE(T);
     // Invoke main which will return the port to which load requests are sent.
     const Library& root_library =
-        Library::Handle(Z, I->object_store()->root_library());
+        Library::Handle(Z, I->group()->object_store()->root_library());
     if (root_library.IsNull()) {
       OS::PrintErr(DART_KERNEL_ISOLATE_NAME
                    ": Embedder did not install a script.");
@@ -563,15 +563,14 @@
     is_static_object.type = Dart_CObject_kBool;
     is_static_object.value.as_bool = is_static;
 
-    Isolate* isolate =
-        Thread::Current() != NULL ? Thread::Current()->isolate() : NULL;
-    ASSERT(isolate != NULL);
+    auto isolate = thread->isolate();
+    auto isolate_group = thread->isolate_group();
+    auto source = isolate_group->source();
+
     Dart_CObject isolate_id;
     isolate_id.type = Dart_CObject_kInt64;
-    isolate_id.value.as_int64 =
-        isolate != NULL ? static_cast<int64_t>(isolate->main_port()) : 0;
+    isolate_id.value.as_int64 = static_cast<int64_t>(isolate->main_port());
 
-    IsolateGroupSource* source = Isolate::Current()->source();
     intptr_t num_dills = 0;
     if (source->kernel_buffer != nullptr) {
       num_dills++;
@@ -628,8 +627,7 @@
 
     Dart_CObject enable_asserts;
     enable_asserts.type = Dart_CObject_kBool;
-    enable_asserts.value.as_bool =
-        isolate != NULL ? isolate->asserts() : FLAG_enable_asserts;
+    enable_asserts.value.as_bool = isolate_group->asserts();
 
     intptr_t num_experimental_flags = experimental_flags->length();
     Dart_CObject** experimental_flags_array =
@@ -770,7 +768,7 @@
     Dart_CObject isolate_id;
     isolate_id.type = Dart_CObject_kInt64;
     isolate_id.value.as_int64 =
-        isolate != NULL ? static_cast<int64_t>(isolate->main_port()) : 0;
+        isolate != nullptr ? static_cast<int64_t>(isolate->main_port()) : 0;
 
     Dart_CObject message;
     message.type = Dart_CObject_kArray;
@@ -783,8 +781,9 @@
 
     Dart_CObject enable_asserts;
     enable_asserts.type = Dart_CObject_kBool;
-    enable_asserts.value.as_bool =
-        isolate != NULL ? isolate->asserts() : FLAG_enable_asserts;
+    enable_asserts.value.as_bool = isolate_group != nullptr
+                                       ? isolate_group->asserts()
+                                       : FLAG_enable_asserts;
 
     Dart_CObject null_safety;
     null_safety.type = Dart_CObject_kInt32;
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index 4b60444..0d043d2 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -800,9 +800,9 @@
                         : real_library.LookupClassAllowPrivate(klass));
   ASSERT(!real_class.IsNull());
 
-  const intptr_t num_cids = I->class_table()->NumCids();
+  const intptr_t num_cids = IG->class_table()->NumCids();
   const intptr_t num_libs =
-      GrowableObjectArray::Handle(I->object_store()->libraries()).Length();
+      GrowableObjectArray::Handle(IG->object_store()->libraries()).Length();
 
   // Load the "evaluate:source" expression evaluation library.
   ASSERT(expression_evaluation_library_.IsNull());
@@ -814,9 +814,10 @@
   }
   const Function& function = H.GetExpressionEvaluationFunction();
   ASSERT(!function.IsNull());
-  ASSERT(GrowableObjectArray::Handle(I->object_store()->libraries()).Length() ==
-         num_libs);
-  ASSERT(I->class_table()->NumCids() == num_cids);
+  ASSERT(
+      GrowableObjectArray::Handle(IG->object_store()->libraries()).Length() ==
+      num_libs);
+  ASSERT(IG->class_table()->NumCids() == num_cids);
 
   // Make the expression evaluation function have the right script,
   // kernel data and parent.
@@ -833,7 +834,7 @@
 }
 
 void KernelLoader::FindModifiedLibraries(Program* program,
-                                         Isolate* isolate,
+                                         IsolateGroup* isolate_group,
                                          BitVector* modified_libs,
                                          bool force_reload,
                                          bool* is_empty_program,
@@ -845,8 +846,8 @@
     if (force_reload) {
       // If a reload is being forced we mark all libraries as having
       // been modified.
-      const GrowableObjectArray& libs =
-          GrowableObjectArray::Handle(isolate->object_store()->libraries());
+      const auto& libs = GrowableObjectArray::Handle(
+          isolate_group->object_store()->libraries());
       intptr_t num_libs = libs.Length();
       Library& lib = dart::Library::Handle(zone);
       for (intptr_t i = 0; i < num_libs; i++) {
@@ -1105,7 +1106,7 @@
   // is no longer used.
 
   const GrowableObjectArray& classes =
-      GrowableObjectArray::Handle(Z, I->object_store()->pending_classes());
+      GrowableObjectArray::Handle(Z, IG->object_store()->pending_classes());
 
   // Load all classes.
   intptr_t next_class_offset = library_index.ClassOffset(0);
@@ -2141,7 +2142,7 @@
       Library& lib = Library::Handle(Z);
       Script& script = Script::Handle(Z);
       const GrowableObjectArray& libs =
-          GrowableObjectArray::Handle(isolate_->object_store()->libraries());
+          GrowableObjectArray::Handle(IG->object_store()->libraries());
       for (intptr_t i = 0; i < libs.Length(); i++) {
         lib ^= libs.At(i);
         script = lib.LookupScript(uri_string, /* useResolvedUri = */ true);
diff --git a/runtime/vm/kernel_loader.h b/runtime/vm/kernel_loader.h
index 5b983ea..24e53af 100644
--- a/runtime/vm/kernel_loader.h
+++ b/runtime/vm/kernel_loader.h
@@ -204,7 +204,7 @@
   // not nullptr, then they are populated with number of classes and top-level
   // procedures in [program].
   static void FindModifiedLibraries(Program* program,
-                                    Isolate* isolate,
+                                    IsolateGroup* isolate_group,
                                     BitVector* modified_libs,
                                     bool force_reload,
                                     bool* is_empty_program,
diff --git a/runtime/vm/megamorphic_cache_table.cc b/runtime/vm/megamorphic_cache_table.cc
index 2e7d1b3..76c676f 100644
--- a/runtime/vm/megamorphic_cache_table.cc
+++ b/runtime/vm/megamorphic_cache_table.cc
@@ -16,19 +16,19 @@
 MegamorphicCachePtr MegamorphicCacheTable::Lookup(Thread* thread,
                                                   const String& name,
                                                   const Array& descriptor) {
-  Isolate* isolate = thread->isolate();
+  auto object_store = thread->isolate_group()->object_store();
   SafepointMutexLocker ml(thread->isolate_group()->megamorphic_table_mutex());
 
   ASSERT(name.IsSymbol());
   // TODO(rmacnak): ASSERT(descriptor.IsCanonical());
 
   // TODO(rmacnak): Make a proper hashtable a la symbol table.
-  auto& table = GrowableObjectArray::Handle(
-      isolate->object_store()->megamorphic_cache_table());
+  auto& table =
+      GrowableObjectArray::Handle(object_store->megamorphic_cache_table());
   MegamorphicCache& cache = MegamorphicCache::Handle();
   if (table.IsNull()) {
     table = GrowableObjectArray::New(Heap::kOld);
-    isolate->object_store()->set_megamorphic_cache_table(table);
+    object_store->set_megamorphic_cache_table(table);
   } else {
     for (intptr_t i = 0; i < table.Length(); i++) {
       cache ^= table.At(i);
@@ -46,14 +46,15 @@
 
 void MegamorphicCacheTable::PrintSizes(Isolate* isolate) {
   auto thread = Thread::Current();
-  SafepointMutexLocker ml(thread->isolate_group()->megamorphic_table_mutex());
+  auto isolate_group = thread->isolate_group();
+  SafepointMutexLocker ml(isolate_group->megamorphic_table_mutex());
 
   StackZone zone(thread);
   intptr_t size = 0;
   MegamorphicCache& cache = MegamorphicCache::Handle();
   Array& buckets = Array::Handle();
   const GrowableObjectArray& table = GrowableObjectArray::Handle(
-      isolate->object_store()->megamorphic_cache_table());
+      isolate_group->object_store()->megamorphic_cache_table());
   if (table.IsNull()) return;
   intptr_t max_size = 0;
   for (intptr_t i = 0; i < table.Length(); i++) {
diff --git a/runtime/vm/metrics_test.cc b/runtime/vm/metrics_test.cc
index a26aaac..2881c361 100644
--- a/runtime/vm/metrics_test.cc
+++ b/runtime/vm/metrics_test.cc
@@ -89,8 +89,8 @@
 
   // Ensure we've done new/old GCs to ensure max metrics are initialized.
   String::New("<land-in-new-space>", Heap::kNew);
-  Isolate::Current()->heap()->new_space()->Scavenge();
-  Isolate::Current()->heap()->CollectAllGarbage(Heap::kLowMemory);
+  IsolateGroup::Current()->heap()->new_space()->Scavenge();
+  IsolateGroup::Current()->heap()->CollectAllGarbage(Heap::kLowMemory);
 
   // Ensure we've something live in new space.
   String::New("<land-in-new-space2>", Heap::kNew);
@@ -159,7 +159,7 @@
   EXPECT_STREQ("debugging", last_gcevent_reason);
 
   // This call emits 2 or 3 events.
-  Isolate::Current()->heap()->CollectAllGarbage(Heap::kLowMemory);
+  IsolateGroup::Current()->heap()->CollectAllGarbage(Heap::kLowMemory);
 
   EXPECT_GE(event_counter, 3UL);
   EXPECT_STREQ("MarkCompact", last_gcevent_type);
diff --git a/runtime/vm/native_api_impl.cc b/runtime/vm/native_api_impl.cc
index 1c7c43a..81f2f1e 100644
--- a/runtime/vm/native_api_impl.cc
+++ b/runtime/vm/native_api_impl.cc
@@ -239,13 +239,13 @@
     TransitionNativeToVM _(Thread::Current());
     intptr_t argument = reinterpret_cast<intptr_t>(arg);
     ASSERT(argument > 0);
-    Isolate::Current()->heap()->CollectOnNthAllocation(argument);
+    IsolateGroup::Current()->heap()->CollectOnNthAllocation(argument);
     return nullptr;
 
   } else if (strcmp(command, "gc-now") == 0) {
     ASSERT(arg == nullptr);  // Don't pass an argument to this command.
     TransitionNativeToVM _(Thread::Current());
-    Isolate::Current()->heap()->CollectAllGarbage();
+    IsolateGroup::Current()->heap()->CollectAllGarbage();
     return nullptr;
 
   } else if (strcmp(command, "is-mutator-in-native") == 0) {
@@ -264,9 +264,9 @@
     Thread* const thread = Thread::Current();
     {
       SafepointOperationScope scope(thread);
-      args->isolate->heap()->WriteProtectCode(/*read_only=*/false);
+      args->isolate->group()->heap()->WriteProtectCode(/*read_only=*/false);
       (*args->callback)();
-      args->isolate->heap()->WriteProtectCode(/*read_only=*/true);
+      args->isolate->group()->heap()->WriteProtectCode(/*read_only=*/true);
     }
     Thread::ExitIsolateAsHelper();
     return nullptr;
diff --git a/runtime/vm/native_entry.cc b/runtime/vm/native_entry.cc
index 445ccf0..a299e5d 100644
--- a/runtime/vm/native_entry.cc
+++ b/runtime/vm/native_entry.cc
@@ -74,7 +74,7 @@
   Thread* thread = Thread::Current();
   REUSABLE_GROWABLE_OBJECT_ARRAY_HANDLESCOPE(thread);
   GrowableObjectArray& libs = reused_growable_object_array_handle.Handle();
-  libs = thread->isolate()->object_store()->libraries();
+  libs = thread->isolate_group()->object_store()->libraries();
   ASSERT(!libs.IsNull());
   intptr_t num_libs = libs.Length();
   for (intptr_t i = 0; i < num_libs; i++) {
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 58f2685..57b8bc5 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -513,15 +513,14 @@
   return '\0';
 }
 
-void Object::InitNullAndBool(Isolate* isolate) {
+void Object::InitNullAndBool(IsolateGroup* isolate_group) {
   // Should only be run by the vm isolate.
-  ASSERT(isolate == Dart::vm_isolate());
+  ASSERT(isolate_group == Dart::vm_isolate_group());
+  auto heap = isolate_group->heap();
 
   // TODO(iposva): NoSafepointScope needs to be added here.
   ASSERT(class_class() == null_);
 
-  Heap* heap = isolate->heap();
-
   // Allocate and initialize the null instance.
   // 'null_' must be the first object allocated as it is used in allocation to
   // clear the object.
@@ -657,14 +656,13 @@
   }
 }
 
-void Object::Init(Isolate* isolate) {
+void Object::Init(IsolateGroup* isolate_group) {
   // Should only be run by the vm isolate.
-  ASSERT(isolate == Dart::vm_isolate());
+  ASSERT(isolate_group == Dart::vm_isolate_group());
+  Heap* heap = isolate_group->heap();
 
   InitVtables();
 
-  Heap* heap = isolate->heap();
-
 // Allocate the read only object handles here.
 #define INITIALIZE_SHARED_READONLY_HANDLE(Type, name)                          \
   name##_ = Type::ReadOnlyHandle();
@@ -720,26 +718,26 @@
     cls.set_num_type_arguments(0);
     cls.set_num_native_fields(0);
     cls.InitEmptyFields();
-    isolate->class_table()->Register(cls);
+    isolate_group->class_table()->Register(cls);
   }
 
   // Allocate and initialize the null class.
-  cls = Class::New<Instance, RTN::Instance>(kNullCid, isolate);
+  cls = Class::New<Instance, RTN::Instance>(kNullCid, isolate_group);
   cls.set_num_type_arguments(0);
-  isolate->object_store()->set_null_class(cls);
+  isolate_group->object_store()->set_null_class(cls);
 
   // Allocate and initialize Never class.
-  cls = Class::New<Instance, RTN::Instance>(kNeverCid, isolate);
+  cls = Class::New<Instance, RTN::Instance>(kNeverCid, isolate_group);
   cls.set_num_type_arguments(0);
   cls.set_is_allocate_finalized();
   cls.set_is_declaration_loaded();
   cls.set_is_type_finalized();
-  isolate->object_store()->set_never_class(cls);
+  isolate_group->object_store()->set_never_class(cls);
 
   // Allocate and initialize the free list element class.
-  cls =
-      Class::New<FreeListElement::FakeInstance,
-                 RTN::FreeListElement::FakeInstance>(kFreeListElement, isolate);
+  cls = Class::New<FreeListElement::FakeInstance,
+                   RTN::FreeListElement::FakeInstance>(kFreeListElement,
+                                                       isolate_group);
   cls.set_num_type_arguments(0);
   cls.set_is_allocate_finalized();
   cls.set_is_declaration_loaded();
@@ -748,7 +746,7 @@
   // Allocate and initialize the forwarding corpse class.
   cls = Class::New<ForwardingCorpse::FakeInstance,
                    RTN::ForwardingCorpse::FakeInstance>(kForwardingCorpse,
-                                                        isolate);
+                                                        isolate_group);
   cls.set_num_type_arguments(0);
   cls.set_is_allocate_finalized();
   cls.set_is_declaration_loaded();
@@ -772,146 +770,151 @@
   }
 
   // Allocate the remaining VM internal classes.
-  cls = Class::New<TypeArguments, RTN::TypeArguments>(isolate);
+  cls = Class::New<TypeArguments, RTN::TypeArguments>(isolate_group);
   type_arguments_class_ = cls.raw();
 
-  cls = Class::New<PatchClass, RTN::PatchClass>(isolate);
+  cls = Class::New<PatchClass, RTN::PatchClass>(isolate_group);
   patch_class_class_ = cls.raw();
 
-  cls = Class::New<Function, RTN::Function>(isolate);
+  cls = Class::New<Function, RTN::Function>(isolate_group);
   function_class_ = cls.raw();
 
-  cls = Class::New<ClosureData, RTN::ClosureData>(isolate);
+  cls = Class::New<ClosureData, RTN::ClosureData>(isolate_group);
   closure_data_class_ = cls.raw();
 
-  cls = Class::New<SignatureData, RTN::SignatureData>(isolate);
+  cls = Class::New<SignatureData, RTN::SignatureData>(isolate_group);
   signature_data_class_ = cls.raw();
 
-  cls = Class::New<FfiTrampolineData, RTN::FfiTrampolineData>(isolate);
+  cls = Class::New<FfiTrampolineData, RTN::FfiTrampolineData>(isolate_group);
   ffi_trampoline_data_class_ = cls.raw();
 
-  cls = Class::New<Field, RTN::Field>(isolate);
+  cls = Class::New<Field, RTN::Field>(isolate_group);
   field_class_ = cls.raw();
 
-  cls = Class::New<Script, RTN::Script>(isolate);
+  cls = Class::New<Script, RTN::Script>(isolate_group);
   script_class_ = cls.raw();
 
-  cls = Class::New<Library, RTN::Library>(isolate);
+  cls = Class::New<Library, RTN::Library>(isolate_group);
   library_class_ = cls.raw();
 
-  cls = Class::New<Namespace, RTN::Namespace>(isolate);
+  cls = Class::New<Namespace, RTN::Namespace>(isolate_group);
   namespace_class_ = cls.raw();
 
-  cls = Class::New<KernelProgramInfo, RTN::KernelProgramInfo>(isolate);
+  cls = Class::New<KernelProgramInfo, RTN::KernelProgramInfo>(isolate_group);
   kernel_program_info_class_ = cls.raw();
 
-  cls = Class::New<Code, RTN::Code>(isolate);
+  cls = Class::New<Code, RTN::Code>(isolate_group);
   code_class_ = cls.raw();
 
-  cls = Class::New<Instructions, RTN::Instructions>(isolate);
+  cls = Class::New<Instructions, RTN::Instructions>(isolate_group);
   instructions_class_ = cls.raw();
 
-  cls = Class::New<InstructionsSection, RTN::InstructionsSection>(isolate);
+  cls =
+      Class::New<InstructionsSection, RTN::InstructionsSection>(isolate_group);
   instructions_section_class_ = cls.raw();
 
-  cls = Class::New<ObjectPool, RTN::ObjectPool>(isolate);
+  cls = Class::New<ObjectPool, RTN::ObjectPool>(isolate_group);
   object_pool_class_ = cls.raw();
 
-  cls = Class::New<PcDescriptors, RTN::PcDescriptors>(isolate);
+  cls = Class::New<PcDescriptors, RTN::PcDescriptors>(isolate_group);
   pc_descriptors_class_ = cls.raw();
 
-  cls = Class::New<CodeSourceMap, RTN::CodeSourceMap>(isolate);
+  cls = Class::New<CodeSourceMap, RTN::CodeSourceMap>(isolate_group);
   code_source_map_class_ = cls.raw();
 
-  cls = Class::New<CompressedStackMaps, RTN::CompressedStackMaps>(isolate);
+  cls =
+      Class::New<CompressedStackMaps, RTN::CompressedStackMaps>(isolate_group);
   compressed_stackmaps_class_ = cls.raw();
 
-  cls = Class::New<LocalVarDescriptors, RTN::LocalVarDescriptors>(isolate);
+  cls =
+      Class::New<LocalVarDescriptors, RTN::LocalVarDescriptors>(isolate_group);
   var_descriptors_class_ = cls.raw();
 
-  cls = Class::New<ExceptionHandlers, RTN::ExceptionHandlers>(isolate);
+  cls = Class::New<ExceptionHandlers, RTN::ExceptionHandlers>(isolate_group);
   exception_handlers_class_ = cls.raw();
 
-  cls = Class::New<Context, RTN::Context>(isolate);
+  cls = Class::New<Context, RTN::Context>(isolate_group);
   context_class_ = cls.raw();
 
-  cls = Class::New<ContextScope, RTN::ContextScope>(isolate);
+  cls = Class::New<ContextScope, RTN::ContextScope>(isolate_group);
   context_scope_class_ = cls.raw();
 
-  cls = Class::New<SingleTargetCache, RTN::SingleTargetCache>(isolate);
+  cls = Class::New<SingleTargetCache, RTN::SingleTargetCache>(isolate_group);
   singletargetcache_class_ = cls.raw();
 
-  cls = Class::New<UnlinkedCall, RTN::UnlinkedCall>(isolate);
+  cls = Class::New<UnlinkedCall, RTN::UnlinkedCall>(isolate_group);
   unlinkedcall_class_ = cls.raw();
 
-  cls =
-      Class::New<MonomorphicSmiableCall, RTN::MonomorphicSmiableCall>(isolate);
+  cls = Class::New<MonomorphicSmiableCall, RTN::MonomorphicSmiableCall>(
+      isolate_group);
   monomorphicsmiablecall_class_ = cls.raw();
 
-  cls = Class::New<ICData, RTN::ICData>(isolate);
+  cls = Class::New<ICData, RTN::ICData>(isolate_group);
   icdata_class_ = cls.raw();
 
-  cls = Class::New<MegamorphicCache, RTN::MegamorphicCache>(isolate);
+  cls = Class::New<MegamorphicCache, RTN::MegamorphicCache>(isolate_group);
   megamorphic_cache_class_ = cls.raw();
 
-  cls = Class::New<SubtypeTestCache, RTN::SubtypeTestCache>(isolate);
+  cls = Class::New<SubtypeTestCache, RTN::SubtypeTestCache>(isolate_group);
   subtypetestcache_class_ = cls.raw();
 
-  cls = Class::New<LoadingUnit, RTN::LoadingUnit>(isolate);
+  cls = Class::New<LoadingUnit, RTN::LoadingUnit>(isolate_group);
   loadingunit_class_ = cls.raw();
 
-  cls = Class::New<ApiError, RTN::ApiError>(isolate);
+  cls = Class::New<ApiError, RTN::ApiError>(isolate_group);
   api_error_class_ = cls.raw();
 
-  cls = Class::New<LanguageError, RTN::LanguageError>(isolate);
+  cls = Class::New<LanguageError, RTN::LanguageError>(isolate_group);
   language_error_class_ = cls.raw();
 
-  cls = Class::New<UnhandledException, RTN::UnhandledException>(isolate);
+  cls = Class::New<UnhandledException, RTN::UnhandledException>(isolate_group);
   unhandled_exception_class_ = cls.raw();
 
-  cls = Class::New<UnwindError, RTN::UnwindError>(isolate);
+  cls = Class::New<UnwindError, RTN::UnwindError>(isolate_group);
   unwind_error_class_ = cls.raw();
 
   cls = Class::New<WeakSerializationReference, RTN::WeakSerializationReference>(
-      isolate);
+      isolate_group);
   weak_serialization_reference_class_ = cls.raw();
 
   ASSERT(class_class() != null_);
 
   // Pre-allocate classes in the vm isolate so that we can for example create a
   // symbol table and populate it with some frequently used strings as symbols.
-  cls = Class::New<Array, RTN::Array>(isolate);
-  isolate->object_store()->set_array_class(cls);
+  cls = Class::New<Array, RTN::Array>(isolate_group);
+  isolate_group->object_store()->set_array_class(cls);
   cls.set_type_arguments_field_offset(Array::type_arguments_offset(),
                                       RTN::Array::type_arguments_offset());
   cls.set_num_type_arguments(1);
-  cls = Class::New<Array, RTN::Array>(kImmutableArrayCid, isolate);
-  isolate->object_store()->set_immutable_array_class(cls);
+  cls = Class::New<Array, RTN::Array>(kImmutableArrayCid, isolate_group);
+  isolate_group->object_store()->set_immutable_array_class(cls);
   cls.set_type_arguments_field_offset(Array::type_arguments_offset(),
                                       RTN::Array::type_arguments_offset());
   cls.set_num_type_arguments(1);
-  cls = Class::New<GrowableObjectArray, RTN::GrowableObjectArray>(isolate);
-  isolate->object_store()->set_growable_object_array_class(cls);
+  cls =
+      Class::New<GrowableObjectArray, RTN::GrowableObjectArray>(isolate_group);
+  isolate_group->object_store()->set_growable_object_array_class(cls);
   cls.set_type_arguments_field_offset(
       GrowableObjectArray::type_arguments_offset(),
       RTN::GrowableObjectArray::type_arguments_offset());
   cls.set_num_type_arguments(1);
-  cls = Class::NewStringClass(kOneByteStringCid, isolate);
-  isolate->object_store()->set_one_byte_string_class(cls);
-  cls = Class::NewStringClass(kTwoByteStringCid, isolate);
-  isolate->object_store()->set_two_byte_string_class(cls);
-  cls = Class::New<Mint, RTN::Mint>(isolate);
-  isolate->object_store()->set_mint_class(cls);
-  cls = Class::New<Double, RTN::Double>(isolate);
-  isolate->object_store()->set_double_class(cls);
+  cls = Class::NewStringClass(kOneByteStringCid, isolate_group);
+  isolate_group->object_store()->set_one_byte_string_class(cls);
+  cls = Class::NewStringClass(kTwoByteStringCid, isolate_group);
+  isolate_group->object_store()->set_two_byte_string_class(cls);
+  cls = Class::New<Mint, RTN::Mint>(isolate_group);
+  isolate_group->object_store()->set_mint_class(cls);
+  cls = Class::New<Double, RTN::Double>(isolate_group);
+  isolate_group->object_store()->set_double_class(cls);
 
   // Ensure that class kExternalTypedDataUint8ArrayCid is registered as we
   // need it when reading in the token stream of bootstrap classes in the VM
   // isolate.
-  Class::NewExternalTypedDataClass(kExternalTypedDataUint8ArrayCid, isolate);
+  Class::NewExternalTypedDataClass(kExternalTypedDataUint8ArrayCid,
+                                   isolate_group);
 
   // Needed for object pools of VM isolate stubs.
-  Class::NewTypedDataClass(kTypedDataInt8ArrayCid, isolate);
+  Class::NewTypedDataClass(kTypedDataInt8ArrayCid, isolate_group);
 
   // Allocate and initialize the empty_array instance.
   {
@@ -1036,7 +1039,7 @@
   // as we do not have any VM isolate snapshot at this time.
   *vm_isolate_snapshot_object_table_ = Object::empty_array().raw();
 
-  cls = Class::New<Instance, RTN::Instance>(kDynamicCid, isolate);
+  cls = Class::New<Instance, RTN::Instance>(kDynamicCid, isolate_group);
   cls.set_is_abstract();
   cls.set_num_type_arguments(0);
   cls.set_is_allocate_finalized();
@@ -1044,14 +1047,14 @@
   cls.set_is_type_finalized();
   dynamic_class_ = cls.raw();
 
-  cls = Class::New<Instance, RTN::Instance>(kVoidCid, isolate);
+  cls = Class::New<Instance, RTN::Instance>(kVoidCid, isolate_group);
   cls.set_num_type_arguments(0);
   cls.set_is_allocate_finalized();
   cls.set_is_declaration_loaded();
   cls.set_is_type_finalized();
   void_class_ = cls.raw();
 
-  cls = Class::New<Type, RTN::Type>(isolate);
+  cls = Class::New<Type, RTN::Type>(isolate_group);
   cls.set_is_allocate_finalized();
   cls.set_is_declaration_loaded();
   cls.set_is_type_finalized();
@@ -1083,8 +1086,8 @@
     cls.SetFunctions(Object::empty_array());
   }
 
-  cls = Class::New<Bool, RTN::Bool>(isolate);
-  isolate->object_store()->set_bool_class(cls);
+  cls = Class::New<Bool, RTN::Bool>(isolate_group);
+  isolate_group->object_store()->set_bool_class(cls);
 
   *smi_illegal_cid_ = Smi::New(kIllegalCid);
   *smi_zero_ = Smi::New(0);
@@ -1190,7 +1193,7 @@
   ASSERT(extractor_parameter_names_->IsArray());
 }
 
-void Object::FinishInit(Isolate* isolate) {
+void Object::FinishInit(IsolateGroup* isolate_group) {
   // The type testing stubs we initialize in AbstractType objects for the
   // canonical type of kDynamicCid/kVoidCid need to be set in this
   // method, which is called after StubCode::InitOnce().
@@ -1296,9 +1299,9 @@
   cls = class_name##_class();                                                  \
   cls.set_name(Symbols::name());
 
-void Object::FinalizeVMIsolate(Isolate* isolate) {
+void Object::FinalizeVMIsolate(IsolateGroup* isolate_group) {
   // Should only be run by the vm isolate.
-  ASSERT(isolate == Dart::vm_isolate());
+  ASSERT(isolate_group == Dart::vm_isolate_group());
 
   // Finish initialization of extractor_parameter_names_ which was
   // Started in Object::InitOnce()
@@ -1345,27 +1348,27 @@
   SET_CLASS_NAME(unwind_error, UnwindError);
 
   // Set up names for classes which are also pre-allocated in the vm isolate.
-  cls = isolate->object_store()->array_class();
+  cls = isolate_group->object_store()->array_class();
   cls.set_name(Symbols::_List());
-  cls = isolate->object_store()->one_byte_string_class();
+  cls = isolate_group->object_store()->one_byte_string_class();
   cls.set_name(Symbols::OneByteString());
-  cls = isolate->object_store()->never_class();
+  cls = isolate_group->object_store()->never_class();
   cls.set_name(Symbols::Never());
 
   // Set up names for the pseudo-classes for free list elements and forwarding
   // corpses. Mainly this makes VM debugging easier.
-  cls = isolate->class_table()->At(kFreeListElement);
+  cls = isolate_group->class_table()->At(kFreeListElement);
   cls.set_name(Symbols::FreeListElement());
-  cls = isolate->class_table()->At(kForwardingCorpse);
+  cls = isolate_group->class_table()->At(kForwardingCorpse);
   cls.set_name(Symbols::ForwardingCorpse());
 
   {
-    ASSERT(isolate == Dart::vm_isolate());
+    ASSERT(isolate_group == Dart::vm_isolate_group());
     Thread* thread = Thread::Current();
     WritableVMIsolateScope scope(thread);
     HeapIterationScope iteration(thread);
     FinalizeVMIsolateVisitor premarker;
-    ASSERT(isolate->heap()->UsedInWords(Heap::kNew) == 0);
+    ASSERT(isolate_group->heap()->UsedInWords(Heap::kNew) == 0);
     iteration.IterateOldObjectsNoImagePages(&premarker);
     // Make the VM isolate read-only again after setting all objects as marked.
     // Note objects in image pages are already pre-marked.
@@ -1499,7 +1502,7 @@
   ASSERT(builtin_vtables_[kIllegalCid] == 0);
   ASSERT(builtin_vtables_[kFreeListElement] == 0);
   ASSERT(builtin_vtables_[kForwardingCorpse] == 0);
-  ClassTable* table = Isolate::Current()->class_table();
+  ClassTable* table = IsolateGroup::Current()->class_table();
   for (intptr_t cid = kObjectCid; cid < kNumPredefinedCids; cid++) {
     if (table->HasValidClassAt(cid)) {
       ASSERT(builtin_vtables_[cid] != 0);
@@ -1538,12 +1541,12 @@
 //
 // A non-NULL kernel argument indicates (1).
 // A NULL kernel indicates (2) or (3).
-ErrorPtr Object::Init(Isolate* isolate,
+ErrorPtr Object::Init(IsolateGroup* isolate_group,
                       const uint8_t* kernel_buffer,
                       intptr_t kernel_buffer_size) {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  ASSERT(isolate == thread->isolate());
+  ASSERT(isolate_group == thread->isolate_group());
   TIMELINE_DURATION(thread, Isolate, "Object::Init");
 
 #if defined(DART_PRECOMPILED_RUNTIME)
@@ -1560,8 +1563,8 @@
     // Kernel binary.
     // This will initialize isolate group object_store, shared by all isolates
     // running in the isolate group.
-    ObjectStore* object_store = isolate->object_store();
-    SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+    ObjectStore* object_store = isolate_group->object_store();
+    SafepointWriteRwLocker ml(thread, isolate_group->program_lock());
 
     Class& cls = Class::Handle(zone);
     Type& type = Type::Handle(zone);
@@ -1571,7 +1574,7 @@
 
     // All RawArray fields will be initialized to an empty array, therefore
     // initialize array class first.
-    cls = Class::New<Array, RTN::Array>(isolate);
+    cls = Class::New<Array, RTN::Array>(isolate_group);
     ASSERT(object_store->array_class() == Class::null());
     object_store->set_array_class(cls);
 
@@ -1586,7 +1589,8 @@
 
     // Set up the growable object array class (Has to be done after the array
     // class is setup as one of its field is an array object).
-    cls = Class::New<GrowableObjectArray, RTN::GrowableObjectArray>(isolate);
+    cls = Class::New<GrowableObjectArray, RTN::GrowableObjectArray>(
+        isolate_group);
     object_store->set_growable_object_array_class(cls);
     cls.set_type_arguments_field_offset(
         GrowableObjectArray::type_arguments_offset(),
@@ -1613,24 +1617,24 @@
 
     // Setup type class early in the process.
     const Class& type_cls =
-        Class::Handle(zone, Class::New<Type, RTN::Type>(isolate));
+        Class::Handle(zone, Class::New<Type, RTN::Type>(isolate_group));
     const Class& type_ref_cls =
-        Class::Handle(zone, Class::New<TypeRef, RTN::TypeRef>(isolate));
+        Class::Handle(zone, Class::New<TypeRef, RTN::TypeRef>(isolate_group));
     const Class& type_parameter_cls = Class::Handle(
-        zone, Class::New<TypeParameter, RTN::TypeParameter>(isolate));
+        zone, Class::New<TypeParameter, RTN::TypeParameter>(isolate_group));
     const Class& library_prefix_cls = Class::Handle(
-        zone, Class::New<LibraryPrefix, RTN::LibraryPrefix>(isolate));
+        zone, Class::New<LibraryPrefix, RTN::LibraryPrefix>(isolate_group));
 
     // Pre-allocate the OneByteString class needed by the symbol table.
-    cls = Class::NewStringClass(kOneByteStringCid, isolate);
+    cls = Class::NewStringClass(kOneByteStringCid, isolate_group);
     object_store->set_one_byte_string_class(cls);
 
     // Pre-allocate the TwoByteString class needed by the symbol table.
-    cls = Class::NewStringClass(kTwoByteStringCid, isolate);
+    cls = Class::NewStringClass(kTwoByteStringCid, isolate_group);
     object_store->set_two_byte_string_class(cls);
 
     // Setup the symbol table for the symbols created in the isolate.
-    Symbols::SetupSymbolTable(isolate);
+    Symbols::SetupSymbolTable(isolate_group);
 
     // Set up the libraries array before initializing the core library.
     const GrowableObjectArray& libraries =
@@ -1638,7 +1642,7 @@
     object_store->set_libraries(libraries);
 
     // Pre-register the core library.
-    Library::InitCoreLibrary(isolate);
+    Library::InitCoreLibrary(isolate_group);
 
     // Basic infrastructure has been setup, initialize the class dictionary.
     const Library& core_lib = Library::Handle(zone, Library::CoreLibrary());
@@ -1675,7 +1679,7 @@
     RegisterPrivateClass(cls, Symbols::_GrowableList(), core_lib);
     pending_classes.Add(cls);
 
-    cls = Class::New<Array, RTN::Array>(kImmutableArrayCid, isolate);
+    cls = Class::New<Array, RTN::Array>(kImmutableArrayCid, isolate_group);
     object_store->set_immutable_array_class(cls);
     cls.set_type_arguments_field_offset(Array::type_arguments_offset(),
                                         RTN::Array::type_arguments_offset());
@@ -1694,12 +1698,12 @@
     RegisterPrivateClass(cls, Symbols::TwoByteString(), core_lib);
     pending_classes.Add(cls);
 
-    cls = Class::NewStringClass(kExternalOneByteStringCid, isolate);
+    cls = Class::NewStringClass(kExternalOneByteStringCid, isolate_group);
     object_store->set_external_one_byte_string_class(cls);
     RegisterPrivateClass(cls, Symbols::ExternalOneByteString(), core_lib);
     pending_classes.Add(cls);
 
-    cls = Class::NewStringClass(kExternalTwoByteStringCid, isolate);
+    cls = Class::NewStringClass(kExternalTwoByteStringCid, isolate_group);
     object_store->set_external_two_byte_string_class(cls);
     RegisterPrivateClass(cls, Symbols::ExternalTwoByteString(), core_lib);
     pending_classes.Add(cls);
@@ -1717,31 +1721,31 @@
     ASSERT(!isolate_lib.IsNull());
     ASSERT(isolate_lib.raw() == Library::IsolateLibrary());
 
-    cls = Class::New<Capability, RTN::Capability>(isolate);
+    cls = Class::New<Capability, RTN::Capability>(isolate_group);
     RegisterPrivateClass(cls, Symbols::_CapabilityImpl(), isolate_lib);
     pending_classes.Add(cls);
 
-    cls = Class::New<ReceivePort, RTN::ReceivePort>(isolate);
+    cls = Class::New<ReceivePort, RTN::ReceivePort>(isolate_group);
     RegisterPrivateClass(cls, Symbols::_RawReceivePortImpl(), isolate_lib);
     pending_classes.Add(cls);
 
-    cls = Class::New<SendPort, RTN::SendPort>(isolate);
+    cls = Class::New<SendPort, RTN::SendPort>(isolate_group);
     RegisterPrivateClass(cls, Symbols::_SendPortImpl(), isolate_lib);
     pending_classes.Add(cls);
 
-    cls =
-        Class::New<TransferableTypedData, RTN::TransferableTypedData>(isolate);
+    cls = Class::New<TransferableTypedData, RTN::TransferableTypedData>(
+        isolate_group);
     RegisterPrivateClass(cls, Symbols::_TransferableTypedDataImpl(),
                          isolate_lib);
     pending_classes.Add(cls);
 
-    const Class& stacktrace_cls =
-        Class::Handle(zone, Class::New<StackTrace, RTN::StackTrace>(isolate));
+    const Class& stacktrace_cls = Class::Handle(
+        zone, Class::New<StackTrace, RTN::StackTrace>(isolate_group));
     RegisterPrivateClass(stacktrace_cls, Symbols::_StackTrace(), core_lib);
     pending_classes.Add(stacktrace_cls);
     // Super type set below, after Object is allocated.
 
-    cls = Class::New<RegExp, RTN::RegExp>(isolate);
+    cls = Class::New<RegExp, RTN::RegExp>(isolate_group);
     RegisterPrivateClass(cls, Symbols::_RegExp(), core_lib);
     pending_classes.Add(cls);
 
@@ -1751,7 +1755,7 @@
     // The script and token index of these pre-allocated classes is set up in
     // the parser when the corelib script is compiled (see
     // Parser::ParseClassDefinition).
-    cls = Class::New<Instance, RTN::Instance>(kInstanceCid, isolate);
+    cls = Class::New<Instance, RTN::Instance>(kInstanceCid, isolate_group);
     object_store->set_object_class(cls);
     cls.set_name(Symbols::Object());
     cls.set_num_type_arguments(0);
@@ -1768,19 +1772,19 @@
     type = type.ToNullability(Nullability::kNullable, Heap::kOld);
     object_store->set_nullable_object_type(type);
 
-    cls = Class::New<Bool, RTN::Bool>(isolate);
+    cls = Class::New<Bool, RTN::Bool>(isolate_group);
     object_store->set_bool_class(cls);
     RegisterClass(cls, Symbols::Bool(), core_lib);
     pending_classes.Add(cls);
 
-    cls = Class::New<Instance, RTN::Instance>(kNullCid, isolate);
+    cls = Class::New<Instance, RTN::Instance>(kNullCid, isolate_group);
     object_store->set_null_class(cls);
     cls.set_num_type_arguments(0);
     cls.set_is_prefinalized();
     RegisterClass(cls, Symbols::Null(), core_lib);
     pending_classes.Add(cls);
 
-    cls = Class::New<Instance, RTN::Instance>(kNeverCid, isolate);
+    cls = Class::New<Instance, RTN::Instance>(kNeverCid, isolate_group);
     cls.set_num_type_arguments(0);
     cls.set_is_allocate_finalized();
     cls.set_is_declaration_loaded();
@@ -1803,33 +1807,33 @@
                          core_lib);
     pending_classes.Add(type_parameter_cls);
 
-    cls = Class::New<Integer, RTN::Integer>(isolate);
+    cls = Class::New<Integer, RTN::Integer>(isolate_group);
     object_store->set_integer_implementation_class(cls);
     RegisterPrivateClass(cls, Symbols::_IntegerImplementation(), core_lib);
     pending_classes.Add(cls);
 
-    cls = Class::New<Smi, RTN::Smi>(isolate);
+    cls = Class::New<Smi, RTN::Smi>(isolate_group);
     object_store->set_smi_class(cls);
     RegisterPrivateClass(cls, Symbols::_Smi(), core_lib);
     pending_classes.Add(cls);
 
-    cls = Class::New<Mint, RTN::Mint>(isolate);
+    cls = Class::New<Mint, RTN::Mint>(isolate_group);
     object_store->set_mint_class(cls);
     RegisterPrivateClass(cls, Symbols::_Mint(), core_lib);
     pending_classes.Add(cls);
 
-    cls = Class::New<Double, RTN::Double>(isolate);
+    cls = Class::New<Double, RTN::Double>(isolate_group);
     object_store->set_double_class(cls);
     RegisterPrivateClass(cls, Symbols::_Double(), core_lib);
     pending_classes.Add(cls);
 
     // Class that represents the Dart class _Closure and C++ class Closure.
-    cls = Class::New<Closure, RTN::Closure>(isolate);
+    cls = Class::New<Closure, RTN::Closure>(isolate_group);
     object_store->set_closure_class(cls);
     RegisterPrivateClass(cls, Symbols::_Closure(), core_lib);
     pending_classes.Add(cls);
 
-    cls = Class::New<WeakProperty, RTN::WeakProperty>(isolate);
+    cls = Class::New<WeakProperty, RTN::WeakProperty>(isolate_group);
     object_store->set_weak_property_class(cls);
     RegisterPrivateClass(cls, Symbols::_WeakProperty(), core_lib);
 
@@ -1846,7 +1850,7 @@
     ASSERT(!lib.IsNull());
     ASSERT(lib.raw() == Library::MirrorsLibrary());
 
-    cls = Class::New<MirrorReference, RTN::MirrorReference>(isolate);
+    cls = Class::New<MirrorReference, RTN::MirrorReference>(isolate_group);
     RegisterPrivateClass(cls, Symbols::_MirrorReference(), lib);
 #endif
 
@@ -1862,7 +1866,7 @@
     object_store->set_bootstrap_library(ObjectStore::kCollection, lib);
     ASSERT(!lib.IsNull());
     ASSERT(lib.raw() == Library::CollectionLibrary());
-    cls = Class::New<LinkedHashMap, RTN::LinkedHashMap>(isolate);
+    cls = Class::New<LinkedHashMap, RTN::LinkedHashMap>(isolate_group);
     object_store->set_linked_hash_map_class(cls);
     cls.set_type_arguments_field_offset(
         LinkedHashMap::type_arguments_offset(),
@@ -1882,7 +1886,7 @@
     object_store->set_bootstrap_library(ObjectStore::kAsync, lib);
     ASSERT(!lib.IsNull());
     ASSERT(lib.raw() == Library::AsyncLibrary());
-    cls = Class::New<FutureOr, RTN::FutureOr>(isolate);
+    cls = Class::New<FutureOr, RTN::FutureOr>(isolate_group);
     cls.set_type_arguments_field_offset(FutureOr::type_arguments_offset(),
                                         RTN::FutureOr::type_arguments_offset());
     cls.set_num_type_arguments(1);
@@ -1900,13 +1904,13 @@
     object_store->set_bootstrap_library(ObjectStore::kDeveloper, lib);
     ASSERT(!lib.IsNull());
     ASSERT(lib.raw() == Library::DeveloperLibrary());
-    cls = Class::New<UserTag, RTN::UserTag>(isolate);
+    cls = Class::New<UserTag, RTN::UserTag>(isolate_group);
     RegisterPrivateClass(cls, Symbols::_UserTag(), lib);
     pending_classes.Add(cls);
 
     // Setup some default native field classes which can be extended for
     // specifying native fields in dart classes.
-    Library::InitNativeWrappersLibrary(isolate, is_kernel);
+    Library::InitNativeWrappersLibrary(isolate_group, is_kernel);
     ASSERT(object_store->native_wrappers_library() != Library::null());
 
     // Pre-register the typed_data library so the native class implementations
@@ -1921,45 +1925,46 @@
     ASSERT(!lib.IsNull());
     ASSERT(lib.raw() == Library::TypedDataLibrary());
 #define REGISTER_TYPED_DATA_CLASS(clazz)                                       \
-  cls = Class::NewTypedDataClass(kTypedData##clazz##ArrayCid, isolate);        \
+  cls = Class::NewTypedDataClass(kTypedData##clazz##ArrayCid, isolate_group);  \
   RegisterPrivateClass(cls, Symbols::_##clazz##List(), lib);
 
     DART_CLASS_LIST_TYPED_DATA(REGISTER_TYPED_DATA_CLASS);
 #undef REGISTER_TYPED_DATA_CLASS
 #define REGISTER_TYPED_DATA_VIEW_CLASS(clazz)                                  \
-  cls = Class::NewTypedDataViewClass(kTypedData##clazz##ViewCid, isolate);     \
+  cls =                                                                        \
+      Class::NewTypedDataViewClass(kTypedData##clazz##ViewCid, isolate_group); \
   RegisterPrivateClass(cls, Symbols::_##clazz##View(), lib);                   \
   pending_classes.Add(cls);
 
     CLASS_LIST_TYPED_DATA(REGISTER_TYPED_DATA_VIEW_CLASS);
 
-    cls = Class::NewTypedDataViewClass(kByteDataViewCid, isolate);
+    cls = Class::NewTypedDataViewClass(kByteDataViewCid, isolate_group);
     RegisterPrivateClass(cls, Symbols::_ByteDataView(), lib);
     pending_classes.Add(cls);
 
 #undef REGISTER_TYPED_DATA_VIEW_CLASS
 #define REGISTER_EXT_TYPED_DATA_CLASS(clazz)                                   \
   cls = Class::NewExternalTypedDataClass(kExternalTypedData##clazz##Cid,       \
-                                         isolate);                             \
+                                         isolate_group);                       \
   RegisterPrivateClass(cls, Symbols::_External##clazz(), lib);
 
-    cls = Class::New<Instance, RTN::Instance>(kByteBufferCid, isolate,
+    cls = Class::New<Instance, RTN::Instance>(kByteBufferCid, isolate_group,
                                               /*register_class=*/false);
     cls.set_instance_size(0, 0);
     cls.set_next_field_offset(-kWordSize, -compiler::target::kWordSize);
-    isolate->class_table()->Register(cls);
+    isolate_group->class_table()->Register(cls);
     RegisterPrivateClass(cls, Symbols::_ByteBuffer(), lib);
     pending_classes.Add(cls);
 
     CLASS_LIST_TYPED_DATA(REGISTER_EXT_TYPED_DATA_CLASS);
 #undef REGISTER_EXT_TYPED_DATA_CLASS
     // Register Float32x4, Int32x4, and Float64x2 in the object store.
-    cls = Class::New<Float32x4, RTN::Float32x4>(isolate);
+    cls = Class::New<Float32x4, RTN::Float32x4>(isolate_group);
     RegisterPrivateClass(cls, Symbols::_Float32x4(), lib);
     pending_classes.Add(cls);
     object_store->set_float32x4_class(cls);
 
-    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate,
+    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate_group,
                                               /*register_class=*/true,
                                               /*is_abstract=*/true);
     RegisterClass(cls, Symbols::Float32x4(), lib);
@@ -1968,12 +1973,12 @@
     type = Type::NewNonParameterizedType(cls);
     object_store->set_float32x4_type(type);
 
-    cls = Class::New<Int32x4, RTN::Int32x4>(isolate);
+    cls = Class::New<Int32x4, RTN::Int32x4>(isolate_group);
     RegisterPrivateClass(cls, Symbols::_Int32x4(), lib);
     pending_classes.Add(cls);
     object_store->set_int32x4_class(cls);
 
-    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate,
+    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate_group,
                                               /*register_class=*/true,
                                               /*is_abstract=*/true);
     RegisterClass(cls, Symbols::Int32x4(), lib);
@@ -1982,12 +1987,12 @@
     type = Type::NewNonParameterizedType(cls);
     object_store->set_int32x4_type(type);
 
-    cls = Class::New<Float64x2, RTN::Float64x2>(isolate);
+    cls = Class::New<Float64x2, RTN::Float64x2>(isolate_group);
     RegisterPrivateClass(cls, Symbols::_Float64x2(), lib);
     pending_classes.Add(cls);
     object_store->set_float64x2_class(cls);
 
-    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate,
+    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate_group,
                                               /*register_class=*/true,
                                               /*is_abstract=*/true);
     RegisterClass(cls, Symbols::Float64x2(), lib);
@@ -2003,7 +2008,7 @@
 
     // Abstract class that represents the Dart class Type.
     // Note that this class is implemented by Dart class _AbstractType.
-    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate,
+    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate_group,
                                               /*register_class=*/true,
                                               /*is_abstract=*/true);
     cls.set_num_type_arguments(0);
@@ -2014,7 +2019,7 @@
     object_store->set_type_type(type);
 
     // Abstract class that represents the Dart class Function.
-    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate,
+    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate_group,
                                               /*register_class=*/true,
                                               /*is_abstract=*/true);
     cls.set_num_type_arguments(0);
@@ -2028,7 +2033,7 @@
     type = type.ToNullability(Nullability::kNonNullable, Heap::kOld);
     object_store->set_non_nullable_function_type(type);
 
-    cls = Class::New<Number, RTN::Number>(isolate);
+    cls = Class::New<Number, RTN::Number>(isolate_group);
     RegisterClass(cls, Symbols::Number(), core_lib);
     pending_classes.Add(cls);
     type = Type::NewNonParameterizedType(cls);
@@ -2038,7 +2043,7 @@
     type = type.ToNullability(Nullability::kNonNullable, Heap::kOld);
     object_store->set_non_nullable_number_type(type);
 
-    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate,
+    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate_group,
                                               /*register_class=*/true,
                                               /*is_abstract=*/true);
     RegisterClass(cls, Symbols::Int(), core_lib);
@@ -2054,7 +2059,7 @@
     type = type.ToNullability(Nullability::kNullable, Heap::kOld);
     object_store->set_nullable_int_type(type);
 
-    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate,
+    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate_group,
                                               /*register_class=*/true,
                                               /*is_abstract=*/true);
     RegisterClass(cls, Symbols::Double(), core_lib);
@@ -2071,7 +2076,7 @@
     object_store->set_nullable_double_type(type);
 
     name = Symbols::_String().raw();
-    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate,
+    cls = Class::New<Instance, RTN::Instance>(kIllegalCid, isolate_group,
                                               /*register_class=*/true,
                                               /*is_abstract=*/true);
     RegisterClass(cls, name, core_lib);
@@ -2231,7 +2236,7 @@
     }
     object_store->set_bootstrap_library(ObjectStore::kFfi, lib);
 
-    cls = Class::New<Instance, RTN::Instance>(kFfiNativeTypeCid, isolate);
+    cls = Class::New<Instance, RTN::Instance>(kFfiNativeTypeCid, isolate_group);
     cls.set_num_type_arguments(0);
     cls.set_is_prefinalized();
     pending_classes.Add(cls);
@@ -2239,7 +2244,7 @@
     RegisterClass(cls, Symbols::FfiNativeType(), lib);
 
 #define REGISTER_FFI_TYPE_MARKER(clazz)                                        \
-  cls = Class::New<Instance, RTN::Instance>(kFfi##clazz##Cid, isolate);        \
+  cls = Class::New<Instance, RTN::Instance>(kFfi##clazz##Cid, isolate_group);  \
   cls.set_num_type_arguments(0);                                               \
   cls.set_is_prefinalized();                                                   \
   pending_classes.Add(cls);                                                    \
@@ -2247,7 +2252,8 @@
     CLASS_LIST_FFI_TYPE_MARKER(REGISTER_FFI_TYPE_MARKER);
 #undef REGISTER_FFI_TYPE_MARKER
 
-    cls = Class::New<Instance, RTN::Instance>(kFfiNativeFunctionCid, isolate);
+    cls = Class::New<Instance, RTN::Instance>(kFfiNativeFunctionCid,
+                                              isolate_group);
     cls.set_type_arguments_field_offset(Pointer::type_arguments_offset(),
                                         RTN::Pointer::type_arguments_offset());
     cls.set_num_type_arguments(1);
@@ -2255,13 +2261,13 @@
     pending_classes.Add(cls);
     RegisterClass(cls, Symbols::FfiNativeFunction(), lib);
 
-    cls = Class::NewPointerClass(kFfiPointerCid, isolate);
+    cls = Class::NewPointerClass(kFfiPointerCid, isolate_group);
     object_store->set_ffi_pointer_class(cls);
     pending_classes.Add(cls);
     RegisterClass(cls, Symbols::FfiPointer(), lib);
 
     cls = Class::New<DynamicLibrary, RTN::DynamicLibrary>(kFfiDynamicLibraryCid,
-                                                          isolate);
+                                                          isolate_group);
     cls.set_instance_size(DynamicLibrary::InstanceSize(),
                           compiler::target::RoundedAllocationSize(
                               RTN::DynamicLibrary::InstanceSize()));
@@ -2277,7 +2283,7 @@
       return error.raw();
     }
 
-    isolate->class_table()->CopySizesFromClassObjects();
+    isolate_group->class_table()->CopySizesFromClassObjects();
 
     ClassFinalizer::VerifyBootstrapClasses();
 
@@ -2292,7 +2298,7 @@
     const bool injected = cls.InjectCIDFields();
     ASSERT(injected);
 
-    isolate->object_store()->InitKnownObjects();
+    isolate_group->object_store()->InitKnownObjects();
 
     // Set up recognized state of all functions (core, math and typed data).
     MethodRecognizer::InitializeState();
@@ -2301,7 +2307,7 @@
     // Object::Init version when we are running in a version of dart that has a
     // full snapshot linked in and an isolate is initialized using the full
     // snapshot.
-    ObjectStore* object_store = isolate->object_store();
+    ObjectStore* object_store = isolate_group->object_store();
 
     Class& cls = Class::Handle(zone);
 
@@ -2311,122 +2317,124 @@
     // stored in the object store. Yet we still need to create their Class
     // object so that they get put into the class_table (as a side effect of
     // Class::New()).
-    cls = Class::New<Instance, RTN::Instance>(kInstanceCid, isolate);
+    cls = Class::New<Instance, RTN::Instance>(kInstanceCid, isolate_group);
     object_store->set_object_class(cls);
 
-    cls = Class::New<LibraryPrefix, RTN::LibraryPrefix>(isolate);
-    cls = Class::New<Type, RTN::Type>(isolate);
-    cls = Class::New<TypeRef, RTN::TypeRef>(isolate);
-    cls = Class::New<TypeParameter, RTN::TypeParameter>(isolate);
+    cls = Class::New<LibraryPrefix, RTN::LibraryPrefix>(isolate_group);
+    cls = Class::New<Type, RTN::Type>(isolate_group);
+    cls = Class::New<TypeRef, RTN::TypeRef>(isolate_group);
+    cls = Class::New<TypeParameter, RTN::TypeParameter>(isolate_group);
 
-    cls = Class::New<Array, RTN::Array>(isolate);
+    cls = Class::New<Array, RTN::Array>(isolate_group);
     object_store->set_array_class(cls);
 
-    cls = Class::New<Array, RTN::Array>(kImmutableArrayCid, isolate);
+    cls = Class::New<Array, RTN::Array>(kImmutableArrayCid, isolate_group);
     object_store->set_immutable_array_class(cls);
 
-    cls = Class::New<GrowableObjectArray, RTN::GrowableObjectArray>(isolate);
+    cls = Class::New<GrowableObjectArray, RTN::GrowableObjectArray>(
+        isolate_group);
     object_store->set_growable_object_array_class(cls);
 
-    cls = Class::New<LinkedHashMap, RTN::LinkedHashMap>(isolate);
+    cls = Class::New<LinkedHashMap, RTN::LinkedHashMap>(isolate_group);
     object_store->set_linked_hash_map_class(cls);
 
-    cls = Class::New<Float32x4, RTN::Float32x4>(isolate);
+    cls = Class::New<Float32x4, RTN::Float32x4>(isolate_group);
     object_store->set_float32x4_class(cls);
 
-    cls = Class::New<Int32x4, RTN::Int32x4>(isolate);
+    cls = Class::New<Int32x4, RTN::Int32x4>(isolate_group);
     object_store->set_int32x4_class(cls);
 
-    cls = Class::New<Float64x2, RTN::Float64x2>(isolate);
+    cls = Class::New<Float64x2, RTN::Float64x2>(isolate_group);
     object_store->set_float64x2_class(cls);
 
 #define REGISTER_TYPED_DATA_CLASS(clazz)                                       \
-  cls = Class::NewTypedDataClass(kTypedData##clazz##Cid, isolate);
+  cls = Class::NewTypedDataClass(kTypedData##clazz##Cid, isolate_group);
     CLASS_LIST_TYPED_DATA(REGISTER_TYPED_DATA_CLASS);
 #undef REGISTER_TYPED_DATA_CLASS
 #define REGISTER_TYPED_DATA_VIEW_CLASS(clazz)                                  \
-  cls = Class::NewTypedDataViewClass(kTypedData##clazz##ViewCid, isolate);
+  cls = Class::NewTypedDataViewClass(kTypedData##clazz##ViewCid, isolate_group);
     CLASS_LIST_TYPED_DATA(REGISTER_TYPED_DATA_VIEW_CLASS);
 #undef REGISTER_TYPED_DATA_VIEW_CLASS
-    cls = Class::NewTypedDataViewClass(kByteDataViewCid, isolate);
+    cls = Class::NewTypedDataViewClass(kByteDataViewCid, isolate_group);
 #define REGISTER_EXT_TYPED_DATA_CLASS(clazz)                                   \
   cls = Class::NewExternalTypedDataClass(kExternalTypedData##clazz##Cid,       \
-                                         isolate);
+                                         isolate_group);
     CLASS_LIST_TYPED_DATA(REGISTER_EXT_TYPED_DATA_CLASS);
 #undef REGISTER_EXT_TYPED_DATA_CLASS
 
-    cls = Class::New<Instance, RTN::Instance>(kFfiNativeTypeCid, isolate);
+    cls = Class::New<Instance, RTN::Instance>(kFfiNativeTypeCid, isolate_group);
     object_store->set_ffi_native_type_class(cls);
 
 #define REGISTER_FFI_CLASS(clazz)                                              \
-  cls = Class::New<Instance, RTN::Instance>(kFfi##clazz##Cid, isolate);
+  cls = Class::New<Instance, RTN::Instance>(kFfi##clazz##Cid, isolate_group);
     CLASS_LIST_FFI_TYPE_MARKER(REGISTER_FFI_CLASS);
 #undef REGISTER_FFI_CLASS
 
-    cls = Class::New<Instance, RTN::Instance>(kFfiNativeFunctionCid, isolate);
+    cls = Class::New<Instance, RTN::Instance>(kFfiNativeFunctionCid,
+                                              isolate_group);
 
-    cls = Class::NewPointerClass(kFfiPointerCid, isolate);
+    cls = Class::NewPointerClass(kFfiPointerCid, isolate_group);
     object_store->set_ffi_pointer_class(cls);
 
     cls = Class::New<DynamicLibrary, RTN::DynamicLibrary>(kFfiDynamicLibraryCid,
-                                                          isolate);
+                                                          isolate_group);
 
-    cls = Class::New<Instance, RTN::Instance>(kByteBufferCid, isolate,
-                                              /*register_isolate=*/false);
+    cls = Class::New<Instance, RTN::Instance>(kByteBufferCid, isolate_group,
+                                              /*register_isolate_group=*/false);
     cls.set_instance_size_in_words(0, 0);
-    isolate->class_table()->Register(cls);
+    isolate_group->class_table()->Register(cls);
 
-    cls = Class::New<Integer, RTN::Integer>(isolate);
+    cls = Class::New<Integer, RTN::Integer>(isolate_group);
     object_store->set_integer_implementation_class(cls);
 
-    cls = Class::New<Smi, RTN::Smi>(isolate);
+    cls = Class::New<Smi, RTN::Smi>(isolate_group);
     object_store->set_smi_class(cls);
 
-    cls = Class::New<Mint, RTN::Mint>(isolate);
+    cls = Class::New<Mint, RTN::Mint>(isolate_group);
     object_store->set_mint_class(cls);
 
-    cls = Class::New<Double, RTN::Double>(isolate);
+    cls = Class::New<Double, RTN::Double>(isolate_group);
     object_store->set_double_class(cls);
 
-    cls = Class::New<Closure, RTN::Closure>(isolate);
+    cls = Class::New<Closure, RTN::Closure>(isolate_group);
     object_store->set_closure_class(cls);
 
-    cls = Class::NewStringClass(kOneByteStringCid, isolate);
+    cls = Class::NewStringClass(kOneByteStringCid, isolate_group);
     object_store->set_one_byte_string_class(cls);
 
-    cls = Class::NewStringClass(kTwoByteStringCid, isolate);
+    cls = Class::NewStringClass(kTwoByteStringCid, isolate_group);
     object_store->set_two_byte_string_class(cls);
 
-    cls = Class::NewStringClass(kExternalOneByteStringCid, isolate);
+    cls = Class::NewStringClass(kExternalOneByteStringCid, isolate_group);
     object_store->set_external_one_byte_string_class(cls);
 
-    cls = Class::NewStringClass(kExternalTwoByteStringCid, isolate);
+    cls = Class::NewStringClass(kExternalTwoByteStringCid, isolate_group);
     object_store->set_external_two_byte_string_class(cls);
 
-    cls = Class::New<Bool, RTN::Bool>(isolate);
+    cls = Class::New<Bool, RTN::Bool>(isolate_group);
     object_store->set_bool_class(cls);
 
-    cls = Class::New<Instance, RTN::Instance>(kNullCid, isolate);
+    cls = Class::New<Instance, RTN::Instance>(kNullCid, isolate_group);
     object_store->set_null_class(cls);
 
-    cls = Class::New<Instance, RTN::Instance>(kNeverCid, isolate);
+    cls = Class::New<Instance, RTN::Instance>(kNeverCid, isolate_group);
     object_store->set_never_class(cls);
 
-    cls = Class::New<Capability, RTN::Capability>(isolate);
-    cls = Class::New<ReceivePort, RTN::ReceivePort>(isolate);
-    cls = Class::New<SendPort, RTN::SendPort>(isolate);
-    cls = Class::New<StackTrace, RTN::StackTrace>(isolate);
-    cls = Class::New<RegExp, RTN::RegExp>(isolate);
-    cls = Class::New<Number, RTN::Number>(isolate);
+    cls = Class::New<Capability, RTN::Capability>(isolate_group);
+    cls = Class::New<ReceivePort, RTN::ReceivePort>(isolate_group);
+    cls = Class::New<SendPort, RTN::SendPort>(isolate_group);
+    cls = Class::New<StackTrace, RTN::StackTrace>(isolate_group);
+    cls = Class::New<RegExp, RTN::RegExp>(isolate_group);
+    cls = Class::New<Number, RTN::Number>(isolate_group);
 
-    cls = Class::New<WeakProperty, RTN::WeakProperty>(isolate);
+    cls = Class::New<WeakProperty, RTN::WeakProperty>(isolate_group);
     object_store->set_weak_property_class(cls);
 
-    cls = Class::New<MirrorReference, RTN::MirrorReference>(isolate);
-    cls = Class::New<UserTag, RTN::UserTag>(isolate);
-    cls = Class::New<FutureOr, RTN::FutureOr>(isolate);
-    cls =
-        Class::New<TransferableTypedData, RTN::TransferableTypedData>(isolate);
+    cls = Class::New<MirrorReference, RTN::MirrorReference>(isolate_group);
+    cls = Class::New<UserTag, RTN::UserTag>(isolate_group);
+    cls = Class::New<FutureOr, RTN::FutureOr>(isolate_group);
+    cls = Class::New<TransferableTypedData, RTN::TransferableTypedData>(
+        isolate_group);
   }
   return Error::null();
 }
@@ -2434,7 +2442,7 @@
 #if defined(DEBUG)
 bool Object::InVMIsolateHeap() const {
   if (FLAG_verify_handles && raw()->ptr()->InVMIsolateHeap()) {
-    Heap* vm_isolate_heap = Dart::vm_isolate()->heap();
+    Heap* vm_isolate_heap = Dart::vm_isolate_group()->heap();
     uword addr = ObjectLayout::ToAddr(raw());
     if (!vm_isolate_heap->Contains(addr)) {
       ASSERT(FLAG_write_protect_code);
@@ -2520,7 +2528,7 @@
     if (FLAG_verify_handles && raw_->IsHeapObject()) {
       Heap* isolate_heap = IsolateGroup::Current()->heap();
       if (!isolate_heap->new_space()->scavenging()) {
-        Heap* vm_isolate_heap = Dart::vm_isolate()->heap();
+        Heap* vm_isolate_heap = Dart::vm_isolate_group()->heap();
         uword addr = ObjectLayout::ToAddr(raw_);
         if (!isolate_heap->Contains(addr) && !vm_isolate_heap->Contains(addr)) {
           ASSERT(FLAG_write_protect_code);
@@ -2553,8 +2561,8 @@
     } else if (thread->top_exit_frame_info() != 0) {
       // Use the preallocated out of memory exception to avoid calling
       // into dart code or allocating any code.
-      const Instance& exception =
-          Instance::Handle(thread->isolate()->object_store()->out_of_memory());
+      const Instance& exception = Instance::Handle(
+          thread->isolate_group()->object_store()->out_of_memory());
       Exceptions::Throw(thread, exception);
       UNREACHABLE();
     } else {
@@ -2727,7 +2735,7 @@
 }
 
 template <class FakeObject, class TargetFakeObject>
-ClassPtr Class::New(Isolate* isolate, bool register_class) {
+ClassPtr Class::New(IsolateGroup* isolate_group, bool register_class) {
   ASSERT(Object::class_class() != Class::null());
   Class& result = Class::Handle();
   {
@@ -2768,7 +2776,7 @@
   NOT_IN_PRECOMPILED(result.set_kernel_offset(0));
   result.InitEmptyFields();
   if (register_class) {
-    isolate->class_table()->Register(result);
+    isolate_group->class_table()->Register(result);
   }
   return result.raw();
 }
@@ -3088,11 +3096,11 @@
   ASSERT(is_declaration_loaded());
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   const intptr_t num_type_params = NumTypeParameters();
 
   if ((super_type() == AbstractType::null()) ||
-      (super_type() == isolate->object_store()->object_type())) {
+      (super_type() == isolate_group->object_store()->object_type())) {
     return num_type_params;
   }
 
@@ -3197,11 +3205,12 @@
 ClassPtr Class::SuperClass(bool original_classes) const {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   if (super_type() == AbstractType::null()) {
     if (id() == kTypeArgumentsCid) {
       // Pretend TypeArguments objects are Dart instances.
-      return isolate->class_table()->At(kInstanceCid);
+      return isolate_group->class_table()->At(kInstanceCid);
     }
     return Class::null();
   }
@@ -3210,7 +3219,7 @@
   if (original_classes) {
     return isolate->GetClassForHeapWalkAt(type_class_id);
   } else {
-    return isolate->class_table()->At(type_class_id);
+    return isolate_group->class_table()->At(type_class_id);
   }
 }
 
@@ -3271,10 +3280,9 @@
     set_num_native_fields(super.num_native_fields());
 
     if (FLAG_precompiled_mode) {
-      host_bitmap = Isolate::Current()
-                        ->group()
-                        ->shared_class_table()
-                        ->GetUnboxedFieldsMapAt(super.id());
+      host_bitmap =
+          IsolateGroup::Current()->shared_class_table()->GetUnboxedFieldsMapAt(
+              super.id());
     }
   }
   // If the super class is parameterized, use the same type_arguments field,
@@ -3587,7 +3595,7 @@
                          const Object& obj,
                          const String& pragma_name,
                          Object* options) {
-  auto I = T->isolate();
+  auto IG = T->isolate_group();
   auto Z = T->zone();
   auto& lib = Library::Handle(Z);
 
@@ -3624,7 +3632,7 @@
   ASSERT(metadata_obj.IsArray());
 
   auto& metadata = Array::Cast(metadata_obj);
-  auto& pragma_class = Class::Handle(Z, I->object_store()->pragma_class());
+  auto& pragma_class = Class::Handle(Z, IG->object_store()->pragma_class());
   auto& pragma_name_field =
       Field::Handle(Z, pragma_class.LookupField(Symbols::name()));
   auto& pragma_options_field =
@@ -3769,9 +3777,9 @@
 
 void Class::Finalize() const {
   auto thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   ASSERT(thread->IsMutatorThread());
-  ASSERT(!isolate->all_classes_finalized());
+  ASSERT(!thread->isolate()->all_classes_finalized());
   ASSERT(!is_finalized());
   // Prefinalized classes have a VM internal representation and no Dart fields.
   // Their instance size  is precomputed and field offsets are known.
@@ -3779,12 +3787,12 @@
     // Compute offsets of instance fields, instance size and bitmap for unboxed
     // fields.
     const auto host_bitmap = CalculateFieldOffsets();
-    if (raw() == isolate->class_table()->At(id())) {
+    if (raw() == isolate_group->class_table()->At(id())) {
       // Sets the new size in the class table.
-      isolate->class_table()->SetAt(id(), raw());
+      isolate_group->class_table()->SetAt(id(), raw());
       if (FLAG_precompiled_mode && !ClassTable::IsTopLevelCid(id())) {
-        isolate->group()->shared_class_table()->SetUnboxedFieldsMapAt(
-            id(), host_bitmap);
+        isolate_group->shared_class_table()->SetUnboxedFieldsMapAt(id(),
+                                                                   host_bitmap);
       }
     }
   }
@@ -3885,9 +3893,9 @@
   DisableCHAOptimizedCode(Class::Handle());
 }
 
-bool Class::TraceAllocation(Isolate* isolate) const {
+bool Class::TraceAllocation(IsolateGroup* isolate_group) const {
 #ifndef PRODUCT
-  auto class_table = isolate->group()->shared_class_table();
+  auto class_table = isolate_group->shared_class_table();
   return class_table->TraceAllocationFor(id());
 #else
   return false;
@@ -3896,10 +3904,10 @@
 
 void Class::SetTraceAllocation(bool trace_allocation) const {
 #ifndef PRODUCT
-  Isolate* isolate = Isolate::Current();
-  const bool changed = trace_allocation != this->TraceAllocation(isolate);
+  auto isolate_group = IsolateGroup::Current();
+  const bool changed = trace_allocation != this->TraceAllocation(isolate_group);
   if (changed) {
-    auto class_table = isolate->group()->shared_class_table();
+    auto class_table = isolate_group->shared_class_table();
     class_table->SetTraceAllocationFor(id(), trace_allocation);
     DisableAllocationStub();
   }
@@ -4444,7 +4452,7 @@
 
 template <class FakeInstance, class TargetFakeInstance>
 ClassPtr Class::New(intptr_t index,
-                    Isolate* isolate,
+                    IsolateGroup* isolate_group,
                     bool register_class,
                     bool is_abstract) {
   Class& result =
@@ -4453,7 +4461,7 @@
     result.set_is_abstract();
   }
   if (register_class) {
-    isolate->class_table()->Register(result);
+    isolate_group->class_table()->Register(result);
   }
   return result.raw();
 }
@@ -4482,7 +4490,8 @@
 }
 
 ClassPtr Class::NewInstanceClass() {
-  return Class::New<Instance, RTN::Instance>(kIllegalCid, Isolate::Current());
+  return Class::New<Instance, RTN::Instance>(kIllegalCid,
+                                             IsolateGroup::Current());
 }
 
 ClassPtr Class::NewNativeWrapper(const Library& library,
@@ -4522,7 +4531,7 @@
   }
 }
 
-ClassPtr Class::NewStringClass(intptr_t class_id, Isolate* isolate) {
+ClassPtr Class::NewStringClass(intptr_t class_id, IsolateGroup* isolate_group) {
   intptr_t host_instance_size, target_instance_size;
   if (class_id == kOneByteStringCid) {
     host_instance_size = OneByteString::InstanceSize();
@@ -4542,8 +4551,8 @@
     target_instance_size = compiler::target::RoundedAllocationSize(
         RTN::ExternalTwoByteString::InstanceSize());
   }
-  Class& result = Class::Handle(
-      New<String, RTN::String>(class_id, isolate, /*register_class=*/false));
+  Class& result = Class::Handle(New<String, RTN::String>(
+      class_id, isolate_group, /*register_class=*/false));
   result.set_instance_size(host_instance_size, target_instance_size);
 
   const intptr_t host_next_field_offset = String::NextFieldOffset();
@@ -4551,17 +4560,18 @@
   result.set_next_field_offset(host_next_field_offset,
                                target_next_field_offset);
   result.set_is_prefinalized();
-  isolate->class_table()->Register(result);
+  isolate_group->class_table()->Register(result);
   return result.raw();
 }
 
-ClassPtr Class::NewTypedDataClass(intptr_t class_id, Isolate* isolate) {
+ClassPtr Class::NewTypedDataClass(intptr_t class_id,
+                                  IsolateGroup* isolate_group) {
   ASSERT(IsTypedDataClassId(class_id));
   const intptr_t host_instance_size = TypedData::InstanceSize();
   const intptr_t target_instance_size =
       compiler::target::RoundedAllocationSize(RTN::TypedData::InstanceSize());
   Class& result = Class::Handle(New<TypedData, RTN::TypedData>(
-      class_id, isolate, /*register_class=*/false));
+      class_id, isolate_group, /*register_class=*/false));
   result.set_instance_size(host_instance_size, target_instance_size);
 
   const intptr_t host_next_field_offset = TypedData::NextFieldOffset();
@@ -4569,17 +4579,18 @@
   result.set_next_field_offset(host_next_field_offset,
                                target_next_field_offset);
   result.set_is_prefinalized();
-  isolate->class_table()->Register(result);
+  isolate_group->class_table()->Register(result);
   return result.raw();
 }
 
-ClassPtr Class::NewTypedDataViewClass(intptr_t class_id, Isolate* isolate) {
+ClassPtr Class::NewTypedDataViewClass(intptr_t class_id,
+                                      IsolateGroup* isolate_group) {
   ASSERT(IsTypedDataViewClassId(class_id));
   const intptr_t host_instance_size = TypedDataView::InstanceSize();
   const intptr_t target_instance_size = compiler::target::RoundedAllocationSize(
       RTN::TypedDataView::InstanceSize());
   Class& result = Class::Handle(New<TypedDataView, RTN::TypedDataView>(
-      class_id, isolate, /*register_class=*/false));
+      class_id, isolate_group, /*register_class=*/false));
   result.set_instance_size(host_instance_size, target_instance_size);
 
   const intptr_t host_next_field_offset = TypedDataView::NextFieldOffset();
@@ -4588,17 +4599,18 @@
   result.set_next_field_offset(host_next_field_offset,
                                target_next_field_offset);
   result.set_is_prefinalized();
-  isolate->class_table()->Register(result);
+  isolate_group->class_table()->Register(result);
   return result.raw();
 }
 
-ClassPtr Class::NewExternalTypedDataClass(intptr_t class_id, Isolate* isolate) {
+ClassPtr Class::NewExternalTypedDataClass(intptr_t class_id,
+                                          IsolateGroup* isolate_group) {
   ASSERT(IsExternalTypedDataClassId(class_id));
   const intptr_t host_instance_size = ExternalTypedData::InstanceSize();
   const intptr_t target_instance_size = compiler::target::RoundedAllocationSize(
       RTN::ExternalTypedData::InstanceSize());
   Class& result = Class::Handle(New<ExternalTypedData, RTN::ExternalTypedData>(
-      class_id, isolate, /*register_class=*/false));
+      class_id, isolate_group, /*register_class=*/false));
 
   const intptr_t host_next_field_offset = ExternalTypedData::NextFieldOffset();
   const intptr_t target_next_field_offset =
@@ -4607,17 +4619,18 @@
   result.set_next_field_offset(host_next_field_offset,
                                target_next_field_offset);
   result.set_is_prefinalized();
-  isolate->class_table()->Register(result);
+  isolate_group->class_table()->Register(result);
   return result.raw();
 }
 
-ClassPtr Class::NewPointerClass(intptr_t class_id, Isolate* isolate) {
+ClassPtr Class::NewPointerClass(intptr_t class_id,
+                                IsolateGroup* isolate_group) {
   ASSERT(IsFfiPointerClassId(class_id));
   intptr_t host_instance_size = Pointer::InstanceSize();
   intptr_t target_instance_size =
       compiler::target::RoundedAllocationSize(RTN::Pointer::InstanceSize());
-  Class& result = Class::Handle(
-      New<Pointer, RTN::Pointer>(class_id, isolate, /*register_class=*/false));
+  Class& result = Class::Handle(New<Pointer, RTN::Pointer>(
+      class_id, isolate_group, /*register_class=*/false));
   result.set_instance_size(host_instance_size, target_instance_size);
   result.set_type_arguments_field_offset(Pointer::type_arguments_offset(),
                                          RTN::Pointer::type_arguments_offset());
@@ -4628,7 +4641,7 @@
   result.set_next_field_offset(host_next_field_offset,
                                target_next_field_offset);
   result.set_is_prefinalized();
-  isolate->class_table()->Register(result);
+  isolate_group->class_table()->Register(result);
   return result.raw();
 }
 
@@ -5089,7 +5102,7 @@
     //     T0 <: T1 iff Future<S0> <: T1 and S0 <: T1
     if (this_cid == kFutureOrCid) {
       // Check Future<S0> <: T1.
-      ObjectStore* object_store = Isolate::Current()->object_store();
+      ObjectStore* object_store = IsolateGroup::Current()->object_store();
       const Class& future_class =
           Class::Handle(zone, object_store->future_class());
       ASSERT(!future_class.IsNull() && future_class.NumTypeParameters() == 1 &&
@@ -5639,7 +5652,8 @@
 
 // Returns an instance of Double or Double::null().
 DoublePtr Class::LookupCanonicalDouble(Zone* zone, double value) const {
-  ASSERT(this->raw() == Isolate::Current()->object_store()->double_class());
+  ASSERT(this->raw() ==
+         IsolateGroup::Current()->object_store()->double_class());
   if (this->constants() == Array::null()) return Double::null();
 
   Double& canonical_value = Double::Handle(zone);
@@ -5651,7 +5665,7 @@
 
 // Returns an instance of Mint or Mint::null().
 MintPtr Class::LookupCanonicalMint(Zone* zone, int64_t value) const {
-  ASSERT(this->raw() == Isolate::Current()->object_store()->mint_class());
+  ASSERT(this->raw() == IsolateGroup::Current()->object_store()->mint_class());
   if (this->constants() == Array::null()) return Mint::null();
 
   Mint& canonical_value = Mint::Handle(zone);
@@ -6497,11 +6511,11 @@
     return TypeArguments::null();
   }
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
-  ObjectStore* object_store = isolate->object_store();
+  auto isolate_group = thread->isolate_group();
+  ObjectStore* object_store = isolate_group->object_store();
   TypeArguments& result = TypeArguments::Handle(zone);
   {
-    SafepointMutexLocker ml(isolate->group()->type_canonicalization_mutex());
+    SafepointMutexLocker ml(isolate_group->type_canonicalization_mutex());
     CanonicalTypeArgumentsSet table(zone,
                                     object_store->canonical_type_arguments());
     result ^= table.GetOrNull(CanonicalTypeArgumentsKey(*this));
@@ -6525,7 +6539,7 @@
     if (IsRecursive()) {
       SetHash(0);
     }
-    SafepointMutexLocker ml(isolate->group()->type_canonicalization_mutex());
+    SafepointMutexLocker ml(isolate_group->type_canonicalization_mutex());
     CanonicalTypeArgumentsSet table(zone,
                                     object_store->canonical_type_arguments());
     // Since we canonicalized some type arguments above we need to lookup
@@ -7017,7 +7031,7 @@
 
 FunctionPtr Function::GetGeneratedClosure() const {
   const auto& closure_functions = GrowableObjectArray::Handle(
-      Isolate::Current()->object_store()->closure_functions());
+      IsolateGroup::Current()->object_store()->closure_functions());
   auto& entry = Object::Handle();
 
   for (auto i = (closure_functions.Length() - 1); i >= 0; i--) {
@@ -7225,7 +7239,7 @@
     Class& scope_class = Class::Handle(Owner());
     if (!scope_class.IsTypedefClass() ||
         (scope_class.signature_function() != raw())) {
-      scope_class = Isolate::Current()->object_store()->closure_class();
+      scope_class = IsolateGroup::Current()->object_store()->closure_class();
     }
     const TypeArguments& signature_type_arguments =
         TypeArguments::Handle(scope_class.type_parameters());
@@ -8711,7 +8725,7 @@
 
 bool Function::IsDynamicClosureCallDispatcher(Thread* thread) const {
   if (!IsInvokeFieldDispatcher()) return false;
-  if (thread->isolate()->object_store()->closure_class() != Owner()) {
+  if (thread->isolate_group()->object_store()->closure_class() != Owner()) {
     return false;
   }
   const auto& handle = String::Handle(thread->zone(), name());
@@ -8813,7 +8827,7 @@
                                     &is_generic_covariant_impl);
 
     Type& object_type = Type::Handle(zone, Type::ObjectType());
-    ObjectStore* object_store = Isolate::Current()->object_store();
+    ObjectStore* object_store = IsolateGroup::Current()->object_store();
     object_type = nnbd_mode() == NNBDMode::kOptedInLib
                       ? object_store->nullable_object_type()
                       : object_store->legacy_object_type();
@@ -10171,18 +10185,19 @@
   result.set_has_pragma(false);
   result.set_static_type_exactness_state(
       StaticTypeExactnessState::NotTracking());
-  Isolate* isolate = Isolate::Current();
+  auto isolate = Isolate::Current();
+  auto isolate_group = isolate->group();
 
 // Use field guards if they are enabled and the isolate has never reloaded.
 // TODO(johnmccutchan): The reload case assumes the worst case (everything is
 // dynamic and possibly null). Attempt to relax this later.
 #if defined(PRODUCT)
   const bool use_guarded_cid =
-      FLAG_precompiled_mode || isolate->use_field_guards();
+      FLAG_precompiled_mode || isolate_group->use_field_guards();
 #else
   const bool use_guarded_cid =
       FLAG_precompiled_mode ||
-      (isolate->use_field_guards() && !isolate->HasAttemptedReload());
+      (isolate_group->use_field_guards() && !isolate->HasAttemptedReload());
 #endif  // !defined(PRODUCT)
   result.set_guarded_cid_unsafe(use_guarded_cid ? kIllegalCid : kDynamicCid);
   result.set_is_nullable_unsafe(use_guarded_cid ? false : true);
@@ -10650,7 +10665,7 @@
   }
 
   const Class& cls =
-      Class::Handle(Isolate::Current()->class_table()->At(guarded_cid()));
+      Class::Handle(IsolateGroup::Current()->class_table()->At(guarded_cid()));
   const char* class_name = String::Handle(cls.Name()).ToCString();
 
   if (IsBuiltinListClassId(guarded_cid()) && !is_nullable() && is_final()) {
@@ -11028,7 +11043,7 @@
 
 void Field::RecordStore(const Object& value) const {
   ASSERT(IsOriginal());
-  if (!Isolate::Current()->use_field_guards()) {
+  if (!IsolateGroup::Current()->use_field_guards()) {
     return;
   }
 
@@ -11142,7 +11157,7 @@
     Library& lib = Library::Handle(zone);
     Script& script = Script::Handle(zone);
     const GrowableObjectArray& libs = GrowableObjectArray::Handle(
-        zone, Isolate::Current()->object_store()->libraries());
+        zone, IsolateGroup::Current()->object_store()->libraries());
     for (intptr_t i = 0; i < libs.Length(); i++) {
       lib ^= libs.At(i);
       script = lib.LookupScript(uri, /* useResolvedUri = */ true);
@@ -11559,9 +11574,9 @@
 LibraryPtr Script::FindLibrary() const {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
-  const GrowableObjectArray& libs =
-      GrowableObjectArray::Handle(zone, isolate->object_store()->libraries());
+  auto isolate_group = thread->isolate_group();
+  const GrowableObjectArray& libs = GrowableObjectArray::Handle(
+      zone, isolate_group->object_store()->libraries());
   Library& lib = Library::Handle(zone);
   Array& scripts = Array::Handle(zone);
   for (intptr_t i = 0; i < libs.Length(); i++) {
@@ -11910,7 +11925,7 @@
   // caches that contain an entry for this name. If the name was previously
   // looked up but could not be resolved, the cache contains a null entry.
   GrowableObjectArray& libs = GrowableObjectArray::Handle(
-      zone, thread->isolate()->object_store()->libraries());
+      zone, thread->isolate_group()->object_store()->libraries());
   Library& lib = Library::Handle(zone);
   intptr_t num_libs = libs.Length();
   for (intptr_t i = 0; i < num_libs; i++) {
@@ -11924,7 +11939,7 @@
 // Invalidate all exported names caches in the isolate.
 void Library::InvalidateExportedNamesCaches() {
   GrowableObjectArray& libs = GrowableObjectArray::Handle(
-      Isolate::Current()->object_store()->libraries());
+      IsolateGroup::Current()->object_store()->libraries());
   Library& lib = Library::Handle();
   intptr_t num_libs = libs.Length();
   for (intptr_t i = 0; i < num_libs; i++) {
@@ -12651,7 +12666,7 @@
   StoreNonPointer(&raw_ptr()->flags_, flags);
 }
 
-void Library::InitCoreLibrary(Isolate* isolate) {
+void Library::InitCoreLibrary(IsolateGroup* isolate_group) {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
   const String& core_lib_url = Symbols::DartCore();
@@ -12659,8 +12674,9 @@
       Library::Handle(zone, Library::NewLibraryHelper(core_lib_url, false));
   core_lib.SetLoadRequested();
   core_lib.Register(thread);
-  isolate->object_store()->set_bootstrap_library(ObjectStore::kCore, core_lib);
-  isolate->object_store()->set_root_library(Library::Handle());
+  isolate_group->object_store()->set_bootstrap_library(ObjectStore::kCore,
+                                                       core_lib);
+  isolate_group->object_store()->set_root_library(Library::Handle());
 }
 
 // Invoke the function, or noSuchMethod if it is null.
@@ -12727,7 +12743,7 @@
       // exception of "main".
       if (obj.IsFunction() && check_is_entrypoint) {
         if (!getter_name.Equals(String::Handle(String::New("main"))) ||
-            raw() != Isolate::Current()->object_store()->root_library()) {
+            raw() != IsolateGroup::Current()->object_store()->root_library()) {
           CHECK_ERROR(Function::Cast(obj).VerifyClosurizedEntryPoint());
         }
       }
@@ -12900,7 +12916,8 @@
       arguments, type_arguments);
 }
 
-void Library::InitNativeWrappersLibrary(Isolate* isolate, bool is_kernel) {
+void Library::InitNativeWrappersLibrary(IsolateGroup* isolate_group,
+                                        bool is_kernel) {
   static const int kNumNativeWrappersClasses = 4;
   COMPILE_ASSERT((kNumNativeWrappersClasses > 0) &&
                  (kNumNativeWrappersClasses < 10));
@@ -12914,7 +12931,7 @@
   native_flds_lib.SetLoadRequested();
   native_flds_lib.Register(thread);
   native_flds_lib.SetLoadInProgress();
-  isolate->object_store()->set_native_wrappers_library(native_flds_lib);
+  isolate_group->object_store()->set_native_wrappers_library(native_flds_lib);
   static const char* const kNativeWrappersClass = "NativeFieldWrapperClass";
   static const int kNameLength = 25;
   ASSERT(kNameLength == (strlen(kNativeWrappersClass) + 1 + 1));
@@ -13014,8 +13031,7 @@
 // Returns library with given url in current isolate, or NULL.
 LibraryPtr Library::LookupLibrary(Thread* thread, const String& url) {
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
-  ObjectStore* object_store = isolate->object_store();
+  ObjectStore* object_store = thread->isolate_group()->object_store();
 
   // Make sure the URL string has an associated hash code
   // to speed up the repeated equality checks.
@@ -13052,13 +13068,13 @@
 void Library::AllocatePrivateKey() const {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
 
 #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
-  if (isolate->group()->IsReloading()) {
+  if (isolate_group->IsReloading()) {
     // When reloading, we need to make sure we use the original private key
     // if this library previously existed.
-    IsolateReloadContext* reload_context = isolate->reload_context();
+    IsolateReloadContext* reload_context = thread->isolate()->reload_context();
     const String& original_key =
         String::Handle(reload_context->FindLibraryPrivateKey(*this));
     if (!original_key.IsNull()) {
@@ -13074,8 +13090,8 @@
   const String& url = String::Handle(zone, this->url());
   intptr_t hash_value = url.Hash() & hash_mask;
 
-  const GrowableObjectArray& libs =
-      GrowableObjectArray::Handle(zone, isolate->object_store()->libraries());
+  const GrowableObjectArray& libs = GrowableObjectArray::Handle(
+      zone, isolate_group->object_store()->libraries());
   intptr_t sequence_value = libs.Length();
 
   char private_key[32];
@@ -13132,9 +13148,9 @@
 LibraryPtr Library::GetLibrary(intptr_t index) {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
-  const GrowableObjectArray& libs =
-      GrowableObjectArray::Handle(zone, isolate->object_store()->libraries());
+  auto isolate_group = thread->isolate_group();
+  const GrowableObjectArray& libs = GrowableObjectArray::Handle(
+      zone, isolate_group->object_store()->libraries());
   ASSERT(!libs.IsNull());
   if ((0 <= index) && (index < libs.Length())) {
     Library& lib = Library::Handle(zone);
@@ -13146,8 +13162,8 @@
 
 void Library::Register(Thread* thread) const {
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
-  ObjectStore* object_store = isolate->object_store();
+  auto isolate_group = thread->isolate_group();
+  ObjectStore* object_store = isolate_group->object_store();
 
   // A library is "registered" in two places:
   // - A growable array mapping from index to library.
@@ -13175,7 +13191,7 @@
 void Library::RegisterLibraries(Thread* thread,
                                 const GrowableObjectArray& libs) {
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   Library& lib = Library::Handle(zone);
   String& lib_url = String::Handle(zone);
 
@@ -13188,66 +13204,66 @@
     map.InsertNewOrGetValue(lib_url, lib);
   }
   // Now remember these in the isolate's object store.
-  isolate->object_store()->set_libraries(libs);
-  isolate->object_store()->set_libraries_map(map.Release());
+  isolate_group->object_store()->set_libraries(libs);
+  isolate_group->object_store()->set_libraries_map(map.Release());
 }
 
 LibraryPtr Library::AsyncLibrary() {
-  return Isolate::Current()->object_store()->async_library();
+  return IsolateGroup::Current()->object_store()->async_library();
 }
 
 LibraryPtr Library::ConvertLibrary() {
-  return Isolate::Current()->object_store()->convert_library();
+  return IsolateGroup::Current()->object_store()->convert_library();
 }
 
 LibraryPtr Library::CoreLibrary() {
-  return Isolate::Current()->object_store()->core_library();
+  return IsolateGroup::Current()->object_store()->core_library();
 }
 
 LibraryPtr Library::CollectionLibrary() {
-  return Isolate::Current()->object_store()->collection_library();
+  return IsolateGroup::Current()->object_store()->collection_library();
 }
 
 LibraryPtr Library::DeveloperLibrary() {
-  return Isolate::Current()->object_store()->developer_library();
+  return IsolateGroup::Current()->object_store()->developer_library();
 }
 
 LibraryPtr Library::FfiLibrary() {
-  return Isolate::Current()->object_store()->ffi_library();
+  return IsolateGroup::Current()->object_store()->ffi_library();
 }
 
 LibraryPtr Library::InternalLibrary() {
-  return Isolate::Current()->object_store()->_internal_library();
+  return IsolateGroup::Current()->object_store()->_internal_library();
 }
 
 LibraryPtr Library::IsolateLibrary() {
-  return Isolate::Current()->object_store()->isolate_library();
+  return IsolateGroup::Current()->object_store()->isolate_library();
 }
 
 LibraryPtr Library::MathLibrary() {
-  return Isolate::Current()->object_store()->math_library();
+  return IsolateGroup::Current()->object_store()->math_library();
 }
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
 LibraryPtr Library::MirrorsLibrary() {
-  return Isolate::Current()->object_store()->mirrors_library();
+  return IsolateGroup::Current()->object_store()->mirrors_library();
 }
 #endif
 
 LibraryPtr Library::NativeWrappersLibrary() {
-  return Isolate::Current()->object_store()->native_wrappers_library();
+  return IsolateGroup::Current()->object_store()->native_wrappers_library();
 }
 
 LibraryPtr Library::ProfilerLibrary() {
-  return Isolate::Current()->object_store()->profiler_library();
+  return IsolateGroup::Current()->object_store()->profiler_library();
 }
 
 LibraryPtr Library::TypedDataLibrary() {
-  return Isolate::Current()->object_store()->typed_data_library();
+  return IsolateGroup::Current()->object_store()->typed_data_library();
 }
 
 LibraryPtr Library::VMServiceLibrary() {
-  return Isolate::Current()->object_store()->_vmservice_library();
+  return IsolateGroup::Current()->object_store()->_vmservice_library();
 }
 
 const char* Library::ToCString() const {
@@ -13639,7 +13655,7 @@
   Zone* zone = thread->zone();
   Error& error = Error::Handle(zone);
   const GrowableObjectArray& libs = GrowableObjectArray::Handle(
-      Isolate::Current()->object_store()->libraries());
+      IsolateGroup::Current()->object_store()->libraries());
   Library& lib = Library::Handle(zone);
   Class& cls = Class::Handle(zone);
   for (int i = 0; i < libs.Length(); i++) {
@@ -13665,7 +13681,7 @@
   // closures until we have reached the end of the "worklist".
   Object& result = Object::Handle(zone);
   const GrowableObjectArray& closures = GrowableObjectArray::Handle(
-      zone, Isolate::Current()->object_store()->closure_functions());
+      zone, IsolateGroup::Current()->object_store()->closure_functions());
   Function& func = Function::Handle(zone);
   for (int i = 0; i < closures.Length(); i++) {
     func ^= closures.At(i);
@@ -13688,7 +13704,7 @@
   Zone* zone = thread->zone();
   Error& error = Error::Handle(zone);
   const GrowableObjectArray& libs = GrowableObjectArray::Handle(
-      Isolate::Current()->object_store()->libraries());
+      IsolateGroup::Current()->object_store()->libraries());
   Library& lib = Library::Handle(zone);
   Class& cls = Class::Handle(zone);
   for (int i = 0; i < libs.Length(); i++) {
@@ -14218,7 +14234,7 @@
           // Only look up the global table if the map will end up using it.
           maps.UsesGlobalTable() ? CompressedStackMaps::Handle(
                                        thread->zone(),
-                                       thread->isolate()
+                                       thread->isolate_group()
                                            ->object_store()
                                            ->canonicalized_stack_map_entries())
                                  : Object::null_compressed_stackmaps()) {}
@@ -15151,7 +15167,7 @@
            target.name();
   }
 #endif
-  ObjectStore* store = Isolate::Current()->object_store();
+  ObjectStore* store = IsolateGroup::Current()->object_store();
   ASSERT((target.raw() == store->simple_instance_of_true_function()) ||
          (target.raw() == store->simple_instance_of_false_function()));
   const String& instance_of_name = String::Handle(
@@ -16094,7 +16110,7 @@
 ObjectPoolPtr Code::GetObjectPool() const {
 #if defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    return Isolate::Current()->object_store()->global_object_pool();
+    return IsolateGroup::Current()->object_store()->global_object_pool();
   }
 #endif
   return object_pool();
@@ -16530,14 +16546,15 @@
   return CodeLayout::ContainsPC(raw_obj, pc_);
 }
 
-CodePtr Code::LookupCodeInIsolate(Isolate* isolate, uword pc) {
-  ASSERT((isolate == Isolate::Current()) || (isolate == Dart::vm_isolate()));
-  if (isolate->heap() == NULL) {
+CodePtr Code::LookupCodeInIsolateGroup(IsolateGroup* isolate_group, uword pc) {
+  ASSERT((isolate_group == IsolateGroup::Current()) ||
+         (isolate_group == Dart::vm_isolate_group()));
+  if (isolate_group->heap() == NULL) {
     return Code::null();
   }
   HeapIterationScope heap_iteration_scope(Thread::Current());
   SlowFindRawCodeVisitor visitor(pc);
-  ObjectPtr needle = isolate->heap()->FindOldObject(&visitor);
+  ObjectPtr needle = isolate_group->heap()->FindOldObject(&visitor);
   if (needle != Code::null()) {
     return static_cast<CodePtr>(needle);
   }
@@ -16545,11 +16562,11 @@
 }
 
 CodePtr Code::LookupCode(uword pc) {
-  return LookupCodeInIsolate(Isolate::Current(), pc);
+  return LookupCodeInIsolateGroup(IsolateGroup::Current(), pc);
 }
 
 CodePtr Code::LookupCodeInVmIsolate(uword pc) {
-  return LookupCodeInIsolate(Dart::vm_isolate(), pc);
+  return LookupCodeInIsolateGroup(Dart::vm_isolate_group(), pc);
 }
 
 // Given a pc and a timestamp, lookup the code.
@@ -17814,14 +17831,15 @@
 
 const char* UnhandledException::ToErrorCString() const {
   Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
+  auto isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   NoReloadScope no_reload_scope(isolate, thread);
   HANDLESCOPE(thread);
   Object& strtmp = Object::Handle();
   const char* exc_str;
-  if (exception() == isolate->object_store()->out_of_memory()) {
+  if (exception() == isolate_group->object_store()->out_of_memory()) {
     exc_str = "Out of Memory";
-  } else if (exception() == isolate->object_store()->stack_overflow()) {
+  } else if (exception() == isolate_group->object_store()->stack_overflow()) {
     exc_str = "Stack Overflow";
   } else {
     const Instance& exc = Instance::Handle(exception());
@@ -18132,7 +18150,7 @@
   Instance& member = Instance::Handle();
 
   const auto unboxed_fields_bitmap =
-      thread->isolate()->group()->shared_class_table()->GetUnboxedFieldsMapAt(
+      thread->isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt(
           GetClassId());
 
   for (intptr_t offset = Instance::NextFieldOffset();
@@ -18202,7 +18220,7 @@
   } else {
 #if defined(DEBUG)
     // Make sure that we are not missing any fields.
-    CheckForPointers has_pointers(Isolate::Current()->group());
+    CheckForPointers has_pointers(IsolateGroup::Current());
     this->raw()->ptr()->VisitPointers(&has_pointers);
     ASSERT(!has_pointers.has_pointers());
 #endif  // DEBUG
@@ -18906,14 +18924,14 @@
           .ToNullability(Nullability::kLegacy, space);
     }
     if (cid == kNeverCid && unwrapped_type.IsNonNullable()) {
-      ObjectStore* object_store = Isolate::Current()->object_store();
+      ObjectStore* object_store = IsolateGroup::Current()->object_store();
       const Type& future_never_type =
           Type::Handle(zone, object_store->non_nullable_future_never_type());
       ASSERT(!future_never_type.IsNull());
       return future_never_type.ToNullability(nullability(), space);
     }
     if (cid == kNullCid) {
-      ObjectStore* object_store = Isolate::Current()->object_store();
+      ObjectStore* object_store = IsolateGroup::Current()->object_store();
       ASSERT(object_store->nullable_future_null_type() != Type::null());
       return object_store->nullable_future_null_type();
     }
@@ -19543,7 +19561,7 @@
 }
 
 TypePtr Type::NullType() {
-  return Isolate::Current()->object_store()->null_type();
+  return IsolateGroup::Current()->object_store()->null_type();
 }
 
 TypePtr Type::DynamicType() {
@@ -19555,71 +19573,71 @@
 }
 
 TypePtr Type::NeverType() {
-  return Isolate::Current()->object_store()->never_type();
+  return IsolateGroup::Current()->object_store()->never_type();
 }
 
 TypePtr Type::ObjectType() {
-  return Isolate::Current()->object_store()->object_type();
+  return IsolateGroup::Current()->object_store()->object_type();
 }
 
 TypePtr Type::BoolType() {
-  return Isolate::Current()->object_store()->bool_type();
+  return IsolateGroup::Current()->object_store()->bool_type();
 }
 
 TypePtr Type::IntType() {
-  return Isolate::Current()->object_store()->int_type();
+  return IsolateGroup::Current()->object_store()->int_type();
 }
 
 TypePtr Type::NullableIntType() {
-  return Isolate::Current()->object_store()->nullable_int_type();
+  return IsolateGroup::Current()->object_store()->nullable_int_type();
 }
 
 TypePtr Type::SmiType() {
-  return Isolate::Current()->object_store()->smi_type();
+  return IsolateGroup::Current()->object_store()->smi_type();
 }
 
 TypePtr Type::MintType() {
-  return Isolate::Current()->object_store()->mint_type();
+  return IsolateGroup::Current()->object_store()->mint_type();
 }
 
 TypePtr Type::Double() {
-  return Isolate::Current()->object_store()->double_type();
+  return IsolateGroup::Current()->object_store()->double_type();
 }
 
 TypePtr Type::NullableDouble() {
-  return Isolate::Current()->object_store()->nullable_double_type();
+  return IsolateGroup::Current()->object_store()->nullable_double_type();
 }
 
 TypePtr Type::Float32x4() {
-  return Isolate::Current()->object_store()->float32x4_type();
+  return IsolateGroup::Current()->object_store()->float32x4_type();
 }
 
 TypePtr Type::Float64x2() {
-  return Isolate::Current()->object_store()->float64x2_type();
+  return IsolateGroup::Current()->object_store()->float64x2_type();
 }
 
 TypePtr Type::Int32x4() {
-  return Isolate::Current()->object_store()->int32x4_type();
+  return IsolateGroup::Current()->object_store()->int32x4_type();
 }
 
 TypePtr Type::Number() {
-  return Isolate::Current()->object_store()->number_type();
+  return IsolateGroup::Current()->object_store()->number_type();
 }
 
 TypePtr Type::StringType() {
-  return Isolate::Current()->object_store()->string_type();
+  return IsolateGroup::Current()->object_store()->string_type();
 }
 
 TypePtr Type::ArrayType() {
-  return Isolate::Current()->object_store()->array_type();
+  return IsolateGroup::Current()->object_store()->array_type();
 }
 
 TypePtr Type::DartFunctionType() {
-  return Isolate::Current()->object_store()->function_type();
+  return IsolateGroup::Current()->object_store()->function_type();
 }
 
 TypePtr Type::DartTypeType() {
-  return Isolate::Current()->object_store()->type_type();
+  return IsolateGroup::Current()->object_store()->type_type();
 }
 
 TypePtr Type::NewNonParameterizedType(const Class& type_class) {
@@ -19718,7 +19736,7 @@
 }
 
 ClassPtr Type::type_class() const {
-  return Isolate::Current()->class_table()->At(type_class_id());
+  return IsolateGroup::Current()->class_table()->At(type_class_id());
 }
 
 bool Type::IsInstantiated(Genericity genericity,
@@ -20033,7 +20051,7 @@
     return this->raw();
   }
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
 
   const classid_t cid = type_class_id();
   if (cid == kDynamicCid) {
@@ -20055,7 +20073,7 @@
     Type& type = Type::Handle(zone, cls.declaration_type());
     if (type.IsNull()) {
       ASSERT(!cls.raw()->ptr()->InVMIsolateHeap() ||
-             (isolate == Dart::vm_isolate()));
+             (isolate_group == Dart::vm_isolate_group()));
       // Canonicalize the type arguments of the supertype, if any.
       TypeArguments& type_args = TypeArguments::Handle(zone, arguments());
       type_args = type_args.Canonicalize(thread, trail);
@@ -20068,8 +20086,7 @@
       type = cls.declaration_type();
       // May be set while canonicalizing type args.
       if (type.IsNull()) {
-        SafepointMutexLocker ml(
-            isolate->group()->type_canonicalization_mutex());
+        SafepointMutexLocker ml(isolate_group->type_canonicalization_mutex());
         // Recheck if type exists.
         type = cls.declaration_type();
         if (type.IsNull()) {
@@ -20093,9 +20110,9 @@
   }
 
   AbstractType& type = Type::Handle(zone);
-  ObjectStore* object_store = isolate->object_store();
+  ObjectStore* object_store = isolate_group->object_store();
   {
-    SafepointMutexLocker ml(isolate->group()->type_canonicalization_mutex());
+    SafepointMutexLocker ml(isolate_group->type_canonicalization_mutex());
     CanonicalTypeSet table(zone, object_store->canonical_types());
     type ^= table.GetOrNull(CanonicalTypeKey(*this));
     ASSERT(object_store->canonical_types() == table.Release().raw());
@@ -20143,7 +20160,7 @@
 
     // Check to see if the type got added to canonical list as part of the
     // type arguments canonicalization.
-    SafepointMutexLocker ml(isolate->group()->type_canonicalization_mutex());
+    SafepointMutexLocker ml(isolate_group->type_canonicalization_mutex());
     CanonicalTypeSet table(zone, object_store->canonical_types());
     type ^= table.GetOrNull(CanonicalTypeKey(*this));
     if (type.IsNull()) {
@@ -20176,7 +20193,7 @@
     return (raw() == Object::void_type().raw());
   }
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   AbstractType& type = Type::Handle(zone);
   const Class& cls = Class::Handle(zone, type_class());
 
@@ -20188,9 +20205,9 @@
     return (raw() == type.raw());
   }
 
-  ObjectStore* object_store = isolate->object_store();
+  ObjectStore* object_store = isolate_group->object_store();
   {
-    SafepointMutexLocker ml(isolate->group()->type_canonicalization_mutex());
+    SafepointMutexLocker ml(isolate_group->type_canonicalization_mutex());
     CanonicalTypeSet table(zone, object_store->canonical_types());
     type ^= table.GetOrNull(CanonicalTypeKey(*this));
     object_store->set_canonical_types(table.Release());
@@ -20695,7 +20712,7 @@
   if (cid == kFunctionCid) {
     return Class::null();
   }
-  return Isolate::Current()->class_table()->At(cid);
+  return IsolateGroup::Current()->class_table()->At(cid);
 }
 
 void TypeParameter::set_parameterized_function(const Function& value) const {
@@ -20798,7 +20815,7 @@
     return this->raw();
   }
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
 
   const Class& cls = Class::Handle(zone, parameterized_class());
   const Function& function = Function::Handle(
@@ -20819,9 +20836,9 @@
     return type_parameter.raw();
   }
 
-  ObjectStore* object_store = isolate->object_store();
+  ObjectStore* object_store = isolate_group->object_store();
   {
-    SafepointMutexLocker ml(isolate->group()->type_canonicalization_mutex());
+    SafepointMutexLocker ml(isolate_group->type_canonicalization_mutex());
     CanonicalTypeParameterSet table(zone,
                                     object_store->canonical_type_parameters());
     type_parameter ^= table.GetOrNull(CanonicalTypeParameterKey(*this));
@@ -20847,7 +20864,7 @@
 #if defined(DEBUG)
 bool TypeParameter::CheckIsCanonical(Thread* thread) const {
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
 
   const Class& cls = Class::Handle(zone, parameterized_class());
   const Function& function = Function::Handle(
@@ -20867,9 +20884,9 @@
     return (raw() == type_parameter.raw());
   }
 
-  ObjectStore* object_store = isolate->object_store();
+  ObjectStore* object_store = isolate_group->object_store();
   {
-    SafepointMutexLocker ml(isolate->group()->type_canonicalization_mutex());
+    SafepointMutexLocker ml(isolate_group->type_canonicalization_mutex());
     CanonicalTypeParameterSet table(zone,
                                     object_store->canonical_type_parameters());
     type_parameter ^= table.GetOrNull(CanonicalTypeParameterKey(*this));
@@ -21356,7 +21373,7 @@
 }
 
 ClassPtr Smi::Class() {
-  return Isolate::Current()->object_store()->smi_class();
+  return IsolateGroup::Current()->object_store()->smi_class();
 }
 
 void Mint::set_value(int64_t value) const {
@@ -21366,7 +21383,8 @@
 MintPtr Mint::New(int64_t val, Heap::Space space) {
   // Do not allocate a Mint if Smi would do.
   ASSERT(!Smi::IsValid(val));
-  ASSERT(Isolate::Current()->object_store()->mint_class() != Class::null());
+  ASSERT(IsolateGroup::Current()->object_store()->mint_class() !=
+         Class::null());
   Mint& result = Mint::Handle();
   {
     ObjectPtr raw =
@@ -21389,8 +21407,9 @@
   // Do not allocate a Mint if Smi would do.
   ASSERT(!Smi::IsValid(value));
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
-  const Class& cls = Class::Handle(zone, isolate->object_store()->mint_class());
+  auto isolate_group = thread->isolate_group();
+  const Class& cls =
+      Class::Handle(zone, isolate_group->object_store()->mint_class());
   Mint& canonical_value =
       Mint::Handle(zone, cls.LookupCanonicalMint(zone, value));
   if (!canonical_value.IsNull()) {
@@ -21486,7 +21505,8 @@
 }
 
 DoublePtr Double::New(double d, Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->double_class() != Class::null());
+  ASSERT(IsolateGroup::Current()->object_store()->double_class() !=
+         Class::null());
   Double& result = Double::Handle();
   {
     ObjectPtr raw =
@@ -21515,9 +21535,9 @@
 
 DoublePtr Double::NewCanonicalLocked(Thread* thread, double value) {
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   const Class& cls =
-      Class::Handle(zone, isolate->object_store()->double_class());
+      Class::Handle(zone, isolate_group->object_store()->double_class());
   // Linear search to see whether this value is already present in the
   // list of canonicalized constants.
   Double& canonical_value =
@@ -22340,8 +22360,8 @@
                                                  Dart_HandleFinalizer callback,
                                                  intptr_t external_size) {
   ASSERT(callback != NULL);
-  return FinalizablePersistentHandle::New(Isolate::Current(), referent, peer,
-                                          callback, external_size,
+  return FinalizablePersistentHandle::New(IsolateGroup::Current(), referent,
+                                          peer, callback, external_size,
                                           /*auto_delete=*/true);
 }
 
@@ -22591,9 +22611,9 @@
 }
 
 OneByteStringPtr OneByteString::New(intptr_t len, Heap::Space space) {
-  ASSERT((Isolate::Current() == Dart::vm_isolate()) ||
-         ((Isolate::Current()->object_store() != NULL) &&
-          (Isolate::Current()->object_store()->one_byte_string_class() !=
+  ASSERT((IsolateGroup::Current() == Dart::vm_isolate_group()) ||
+         ((IsolateGroup::Current()->object_store() != NULL) &&
+          (IsolateGroup::Current()->object_store()->one_byte_string_class() !=
            Class::null())));
   if (len < 0 || len > kMaxElements) {
     // This should be caught before we reach here.
@@ -22799,7 +22819,7 @@
 }
 
 TwoByteStringPtr TwoByteString::New(intptr_t len, Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->two_byte_string_class() !=
+  ASSERT(IsolateGroup::Current()->object_store()->two_byte_string_class() !=
          nullptr);
   if (len < 0 || len > kMaxElements) {
     // This should be caught before we reach here.
@@ -22953,8 +22973,9 @@
     intptr_t external_allocation_size,
     Dart_HandleFinalizer callback,
     Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->external_one_byte_string_class() !=
-         Class::null());
+  ASSERT(IsolateGroup::Current()
+             ->object_store()
+             ->external_one_byte_string_class() != Class::null());
   if (len < 0 || len > kMaxElements) {
     // This should be caught before we reach here.
     FATAL1("Fatal error in ExternalOneByteString::New: invalid len %" Pd "\n",
@@ -22982,8 +23003,9 @@
     intptr_t external_allocation_size,
     Dart_HandleFinalizer callback,
     Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->external_two_byte_string_class() !=
-         Class::null());
+  ASSERT(IsolateGroup::Current()
+             ->object_store()
+             ->external_two_byte_string_class() != Class::null());
   if (len < 0 || len > kMaxElements) {
     // This should be caught before we reach here.
     FATAL1("Fatal error in ExternalTwoByteString::New: invalid len %" Pd "\n",
@@ -23005,7 +23027,8 @@
 }
 
 BoolPtr Bool::New(bool value) {
-  ASSERT(Isolate::Current()->object_store()->bool_class() != Class::null());
+  ASSERT(IsolateGroup::Current()->object_store()->bool_class() !=
+         Class::null());
   Bool& result = Bool::Handle();
   {
     // Since the two boolean instances are singletons we allocate them straight
@@ -23085,7 +23108,8 @@
 }
 
 ArrayPtr Array::New(intptr_t len, Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->array_class() != Class::null());
+  ASSERT(IsolateGroup::Current()->object_store()->array_class() !=
+         Class::null());
   ArrayPtr result = New(kClassId, len, space);
   if (UseCardMarkingForAllocation(len)) {
     ASSERT(result->IsOldObject());
@@ -23265,7 +23289,7 @@
 }
 
 ImmutableArrayPtr ImmutableArray::New(intptr_t len, Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->immutable_array_class() !=
+  ASSERT(IsolateGroup::Current()->object_store()->immutable_array_class() !=
          Class::null());
   return static_cast<ImmutableArrayPtr>(Array::New(kClassId, len, space));
 }
@@ -23316,8 +23340,9 @@
 
 GrowableObjectArrayPtr GrowableObjectArray::New(const Array& array,
                                                 Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->growable_object_array_class() !=
-         Class::null());
+  ASSERT(
+      IsolateGroup::Current()->object_store()->growable_object_array_class() !=
+      Class::null());
   GrowableObjectArray& result = GrowableObjectArray::Handle();
   {
     ObjectPtr raw =
@@ -23391,7 +23416,7 @@
                                     intptr_t used_data,
                                     intptr_t deleted_keys,
                                     Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->linked_hash_map_class() !=
+  ASSERT(IsolateGroup::Current()->object_store()->linked_hash_map_class() !=
          Class::null());
   LinkedHashMap& result =
       LinkedHashMap::Handle(LinkedHashMap::NewUninitialized(space));
@@ -23404,7 +23429,7 @@
 }
 
 LinkedHashMapPtr LinkedHashMap::NewUninitialized(Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->linked_hash_map_class() !=
+  ASSERT(IsolateGroup::Current()->object_store()->linked_hash_map_class() !=
          Class::null());
   LinkedHashMap& result = LinkedHashMap::Handle();
   {
@@ -23431,7 +23456,7 @@
                             float v2,
                             float v3,
                             Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->float32x4_class() !=
+  ASSERT(IsolateGroup::Current()->object_store()->float32x4_class() !=
          Class::null());
   Float32x4& result = Float32x4::Handle();
   {
@@ -23448,7 +23473,7 @@
 }
 
 Float32x4Ptr Float32x4::New(simd128_value_t value, Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->float32x4_class() !=
+  ASSERT(IsolateGroup::Current()->object_store()->float32x4_class() !=
          Class::null());
   Float32x4& result = Float32x4::Handle();
   {
@@ -23517,7 +23542,8 @@
                         int32_t v2,
                         int32_t v3,
                         Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->int32x4_class() != Class::null());
+  ASSERT(IsolateGroup::Current()->object_store()->int32x4_class() !=
+         Class::null());
   Int32x4& result = Int32x4::Handle();
   {
     ObjectPtr raw =
@@ -23533,7 +23559,8 @@
 }
 
 Int32x4Ptr Int32x4::New(simd128_value_t value, Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->int32x4_class() != Class::null());
+  ASSERT(IsolateGroup::Current()->object_store()->int32x4_class() !=
+         Class::null());
   Int32x4& result = Int32x4::Handle();
   {
     ObjectPtr raw =
@@ -23597,7 +23624,7 @@
 }
 
 Float64x2Ptr Float64x2::New(double value0, double value1, Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->float64x2_class() !=
+  ASSERT(IsolateGroup::Current()->object_store()->float64x2_class() !=
          Class::null());
   Float64x2& result = Float64x2::Handle();
   {
@@ -23612,7 +23639,7 @@
 }
 
 Float64x2Ptr Float64x2::New(simd128_value_t value, Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->float64x2_class() !=
+  ASSERT(IsolateGroup::Current()->object_store()->float64x2_class() !=
          Class::null());
   Float64x2& result = Float64x2::Handle();
   {
@@ -23833,7 +23860,7 @@
   type_args = type_args.Canonicalize(thread, nullptr);
 
   const Class& cls =
-      Class::Handle(Isolate::Current()->class_table()->At(kFfiPointerCid));
+      Class::Handle(IsolateGroup::Current()->class_table()->At(kFfiPointerCid));
   cls.EnsureIsAllocateFinalized(Thread::Current());
 
   Pointer& result = Pointer::Handle(zone);
@@ -23973,9 +24000,10 @@
   }
   // Set up finalizer so it frees allocated memory if handle is
   // garbage-collected.
-  peer->set_handle(FinalizablePersistentHandle::New(
-      thread->isolate(), result, peer, &TransferableTypedDataFinalizer, length,
-      /*auto_delete=*/true));
+  peer->set_handle(
+      FinalizablePersistentHandle::New(thread->isolate_group(), result, peer,
+                                       &TransferableTypedDataFinalizer, length,
+                                       /*auto_delete=*/true));
 
   return result.raw();
 }
@@ -24347,7 +24375,7 @@
   auto const isolate_instructions = reinterpret_cast<uword>(
       T->isolate_group()->source()->snapshot_instructions);
   auto const vm_instructions = reinterpret_cast<uword>(
-      Dart::vm_isolate()->group()->source()->snapshot_instructions);
+      Dart::vm_isolate_group()->source()->snapshot_instructions);
   if (FLAG_dwarf_stack_traces_mode) {
     const Image isolate_instructions_image(
         reinterpret_cast<const void*>(isolate_instructions));
@@ -24635,7 +24663,7 @@
 }
 
 WeakPropertyPtr WeakProperty::New(Heap::Space space) {
-  ASSERT(Isolate::Current()->object_store()->weak_property_class() !=
+  ASSERT(IsolateGroup::Current()->object_store()->weak_property_class() !=
          Class::null());
   ObjectPtr raw = Object::Allocate(WeakProperty::kClassId,
                                    WeakProperty::InstanceSize(), space);
@@ -24823,7 +24851,7 @@
 
 void DumpTypeTable(Isolate* isolate) {
   OS::PrintErr("canonical types:\n");
-  CanonicalTypeSet table(isolate->object_store()->canonical_types());
+  CanonicalTypeSet table(isolate->group()->object_store()->canonical_types());
   table.Dump();
   table.Release();
 }
@@ -24831,7 +24859,7 @@
 void DumpTypeParameterTable(Isolate* isolate) {
   OS::PrintErr("canonical type parameters (cloned from declarations):\n");
   CanonicalTypeParameterSet table(
-      isolate->object_store()->canonical_type_parameters());
+      isolate->group()->object_store()->canonical_type_parameters());
   table.Dump();
   table.Release();
 }
@@ -24839,26 +24867,26 @@
 void DumpTypeArgumentsTable(Isolate* isolate) {
   OS::PrintErr("canonical type arguments:\n");
   CanonicalTypeArgumentsSet table(
-      isolate->object_store()->canonical_type_arguments());
+      isolate->group()->object_store()->canonical_type_arguments());
   table.Dump();
   table.Release();
 }
 
-EntryPointPragma FindEntryPointPragma(Isolate* I,
+EntryPointPragma FindEntryPointPragma(IsolateGroup* IG,
                                       const Array& metadata,
                                       Field* reusable_field_handle,
                                       Object* pragma) {
   for (intptr_t i = 0; i < metadata.Length(); i++) {
     *pragma = metadata.At(i);
-    if (pragma->clazz() != I->object_store()->pragma_class()) {
+    if (pragma->clazz() != IG->object_store()->pragma_class()) {
       continue;
     }
-    *reusable_field_handle = I->object_store()->pragma_name();
+    *reusable_field_handle = IG->object_store()->pragma_name();
     if (Instance::Cast(*pragma).GetField(*reusable_field_handle) !=
         Symbols::vm_entry_point().raw()) {
       continue;
     }
-    *reusable_field_handle = I->object_store()->pragma_options();
+    *reusable_field_handle = IG->object_store()->pragma_options();
     *pragma = Instance::Cast(*pragma).GetField(*reusable_field_handle);
     if (pragma->raw() == Bool::null() || pragma->raw() == Bool::True().raw()) {
       return EntryPointPragma::kAlways;
@@ -24904,7 +24932,7 @@
   if (metadata.IsError()) return Error::RawCast(metadata.raw());
   ASSERT(!metadata.IsNull() && metadata.IsArray());
   EntryPointPragma pragma =
-      FindEntryPointPragma(Isolate::Current(), Array::Cast(metadata),
+      FindEntryPointPragma(IsolateGroup::Current(), Array::Cast(metadata),
                            &Field::Handle(), &Object::Handle());
   bool is_marked_entrypoint = pragma == EntryPointPragma::kAlways;
   if (!is_marked_entrypoint) {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 75da560..c89a269 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -511,18 +511,18 @@
   }
 
   // Initialize the VM isolate.
-  static void InitNullAndBool(Isolate* isolate);
-  static void Init(Isolate* isolate);
+  static void InitNullAndBool(IsolateGroup* isolate_group);
+  static void Init(IsolateGroup* isolate_group);
   static void InitVtables();
-  static void FinishInit(Isolate* isolate);
-  static void FinalizeVMIsolate(Isolate* isolate);
+  static void FinishInit(IsolateGroup* isolate_group);
+  static void FinalizeVMIsolate(IsolateGroup* isolate_group);
   static void FinalizeReadOnlyObject(ObjectPtr object);
 
   static void Cleanup();
 
   // Initialize a new isolate either from a Kernel IR, from source, or from a
   // snapshot.
-  static ErrorPtr Init(Isolate* isolate,
+  static ErrorPtr Init(IsolateGroup* isolate_group,
                        const uint8_t* kernel_buffer,
                        intptr_t kernel_buffer_size);
 
@@ -1491,7 +1491,7 @@
 
   // Allocate a class used for VM internal objects.
   template <class FakeObject, class TargetFakeObject>
-  static ClassPtr New(Isolate* isolate, bool register_class = true);
+  static ClassPtr New(IsolateGroup* isolate_group, bool register_class = true);
 
   // Allocate instance classes.
   static ClassPtr New(const Library& lib,
@@ -1504,20 +1504,24 @@
                                    int num_fields);
 
   // Allocate the raw string classes.
-  static ClassPtr NewStringClass(intptr_t class_id, Isolate* isolate);
+  static ClassPtr NewStringClass(intptr_t class_id,
+                                 IsolateGroup* isolate_group);
 
   // Allocate the raw TypedData classes.
-  static ClassPtr NewTypedDataClass(intptr_t class_id, Isolate* isolate);
+  static ClassPtr NewTypedDataClass(intptr_t class_id,
+                                    IsolateGroup* isolate_group);
 
   // Allocate the raw TypedDataView/ByteDataView classes.
-  static ClassPtr NewTypedDataViewClass(intptr_t class_id, Isolate* isolate);
+  static ClassPtr NewTypedDataViewClass(intptr_t class_id,
+                                        IsolateGroup* isolate_group);
 
   // Allocate the raw ExternalTypedData classes.
   static ClassPtr NewExternalTypedDataClass(intptr_t class_id,
-                                            Isolate* isolate);
+                                            IsolateGroup* isolate);
 
   // Allocate the raw Pointer classes.
-  static ClassPtr NewPointerClass(intptr_t class_id, Isolate* isolate);
+  static ClassPtr NewPointerClass(intptr_t class_id,
+                                  IsolateGroup* isolate_group);
 
   // Register code that has used CHA for optimization.
   // TODO(srdjan): Also register kind of CHA optimization (e.g.: leaf class,
@@ -1536,7 +1540,7 @@
   ArrayPtr dependent_code() const;
   void set_dependent_code(const Array& array) const;
 
-  bool TraceAllocation(Isolate* isolate) const;
+  bool TraceAllocation(IsolateGroup* isolate_group) const;
   void SetTraceAllocation(bool trace_allocation) const;
 
   void ReplaceEnum(IsolateReloadContext* reload_context,
@@ -1719,7 +1723,7 @@
   // Allocate an instance class which has a VM implementation.
   template <class FakeInstance, class TargetFakeInstance>
   static ClassPtr New(intptr_t id,
-                      Isolate* isolate,
+                      IsolateGroup* isolate_group,
                       bool register_class = true,
                       bool is_abstract = false);
 
@@ -4899,8 +4903,9 @@
   static LibraryPtr LookupLibrary(Thread* thread, const String& url);
   static LibraryPtr GetLibrary(intptr_t index);
 
-  static void InitCoreLibrary(Isolate* isolate);
-  static void InitNativeWrappersLibrary(Isolate* isolate, bool is_kernel_file);
+  static void InitCoreLibrary(IsolateGroup* isolate_group);
+  static void InitNativeWrappersLibrary(IsolateGroup* isolate_group,
+                                        bool is_kernel_file);
 
   static LibraryPtr AsyncLibrary();
   static LibraryPtr ConvertLibrary();
@@ -6619,7 +6624,8 @@
   }
 
   intptr_t BinarySearchInSCallTable(uword pc) const;
-  static CodePtr LookupCodeInIsolate(Isolate* isolate, uword pc);
+  static CodePtr LookupCodeInIsolateGroup(IsolateGroup* isolate_group,
+                                          uword pc);
 
   // New is a private method as RawInstruction and RawCode objects should
   // only be created using the Code::FinalizeCode method. This method creates
@@ -9324,8 +9330,8 @@
                               const uint8_t* data,
                               void* peer) {
     ASSERT(str.IsExternalOneByteString());
-    ASSERT(
-        !Isolate::Current()->heap()->Contains(reinterpret_cast<uword>(data)));
+    ASSERT(!IsolateGroup::Current()->heap()->Contains(
+        reinterpret_cast<uword>(data)));
     str.StoreNonPointer(&raw_ptr(str)->external_data_, data);
     str.StoreNonPointer(&raw_ptr(str)->peer_, peer);
   }
@@ -9418,8 +9424,8 @@
                               const uint16_t* data,
                               void* peer) {
     ASSERT(str.IsExternalTwoByteString());
-    ASSERT(
-        !Isolate::Current()->heap()->Contains(reinterpret_cast<uword>(data)));
+    ASSERT(!IsolateGroup::Current()->heap()->Contains(
+        reinterpret_cast<uword>(data)));
     str.StoreNonPointer(&raw_ptr(str)->external_data_, data);
     str.StoreNonPointer(&raw_ptr(str)->peer_, peer);
   }
@@ -10205,8 +10211,8 @@
   }
 
   void SetData(uint8_t* data) const {
-    ASSERT(
-        !Isolate::Current()->heap()->Contains(reinterpret_cast<uword>(data)));
+    ASSERT(!IsolateGroup::Current()->heap()->Contains(
+        reinterpret_cast<uword>(data)));
     StoreNonPointer(&raw_ptr()->data_, data);
   }
 
@@ -10501,7 +10507,7 @@
   FINAL_HEAP_OBJECT_IMPLEMENTATION(LinkedHashMap, Instance);
 
   // Keep this in sync with Dart implementation (lib/compact_hash.dart).
-  static const intptr_t kInitialIndexBits = 3;
+  static const intptr_t kInitialIndexBits = 2;
   static const intptr_t kInitialIndexSize = 1 << (kInitialIndexBits + 1);
 
   // Allocate a map, but leave all fields set to null.
@@ -11098,7 +11104,7 @@
     return Smi::Class();
   }
   ASSERT(!IsolateGroup::Current()->compaction_in_progress());
-  return Isolate::Current()->class_table()->At(raw()->GetClassId());
+  return IsolateGroup::Current()->class_table()->At(raw()->GetClassId());
 }
 
 DART_FORCE_INLINE
@@ -11115,12 +11121,11 @@
   set_vtable(builtin_vtables_[cid]);
 #if defined(DEBUG)
   if (FLAG_verify_handles && raw_->IsHeapObject()) {
-    Isolate* isolate = Isolate::Current();
-    Heap* isolate_heap = isolate->heap();
+    Heap* isolate_heap = IsolateGroup::Current()->heap();
     // TODO(rmacnak): Remove after rewriting StackFrame::VisitObjectPointers
     // to not use handles.
     if (!isolate_heap->new_space()->scavenging()) {
-      Heap* vm_isolate_heap = Dart::vm_isolate()->heap();
+      Heap* vm_isolate_heap = Dart::vm_isolate_group()->heap();
       uword addr = ObjectLayout::ToAddr(raw_);
       if (!isolate_heap->Contains(addr) && !vm_isolate_heap->Contains(addr)) {
         ASSERT(FLAG_write_protect_code);
@@ -11478,7 +11483,7 @@
 void DumpTypeParameterTable(Isolate* isolate);
 void DumpTypeArgumentsTable(Isolate* isolate);
 
-EntryPointPragma FindEntryPointPragma(Isolate* I,
+EntryPointPragma FindEntryPointPragma(IsolateGroup* isolate_group,
                                       const Array& metadata,
                                       Field* reusable_field_handle,
                                       Object* reusable_object_handle);
diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc
index ac498cc..9f5785a 100644
--- a/runtime/vm/object_graph.cc
+++ b/runtime/vm/object_graph.cc
@@ -186,7 +186,7 @@
   HANDLESCOPE(thread);
   Zone* zone = thread->zone();
   const GrowableObjectArray& libraries = GrowableObjectArray::Handle(
-      zone, thread->isolate()->object_store()->libraries());
+      zone, thread->isolate_group()->object_store()->libraries());
   Library& library = Library::Handle(zone);
   Object& entry = Object::Handle(zone);
   Class& cls = Class::Handle(zone);
@@ -660,7 +660,8 @@
     image_page_ranges_[i].size = 0;
   }
   intptr_t next_offset = 0;
-  OldPage* image_page = Dart::vm_isolate()->heap()->old_space()->image_pages_;
+  OldPage* image_page =
+      Dart::vm_isolate_group()->heap()->old_space()->image_pages_;
   while (image_page != NULL) {
     RELEASE_ASSERT(next_offset <= kMaxImagePages);
     image_page_ranges_[next_offset].base = image_page->object_start();
@@ -669,7 +670,7 @@
     image_page = image_page->next();
     next_offset++;
   }
-  image_page = isolate()->heap()->old_space()->image_pages_;
+  image_page = isolate_group()->heap()->old_space()->image_pages_;
   while (image_page != NULL) {
     RELEASE_ASSERT(next_offset <= kMaxImagePages);
     image_page_ranges_[next_offset].base = image_page->object_start();
@@ -679,7 +680,7 @@
     next_offset++;
   }
 
-  OldPage* page = isolate()->heap()->old_space()->pages_;
+  OldPage* page = isolate_group()->heap()->old_space()->pages_;
   while (page != NULL) {
     page->forwarding_page();
     CountingPage* counting_page =
@@ -1032,7 +1033,7 @@
 
   {
     HANDLESCOPE(thread());
-    ClassTable* class_table = isolate()->class_table();
+    ClassTable* class_table = isolate_group()->class_table();
     class_count_ = class_table->NumCids() - 1;
 
     Class& cls = Class::Handle();
diff --git a/runtime/vm/object_graph_test.cc b/runtime/vm/object_graph_test.cc
index 882c685..1fbe1d4 100644
--- a/runtime/vm/object_graph_test.cc
+++ b/runtime/vm/object_graph_test.cc
@@ -40,7 +40,8 @@
 };
 
 ISOLATE_UNIT_TEST_CASE(ObjectGraph) {
-  Isolate* isolate = thread->isolate();
+  auto heap = thread->isolate_group()->heap();
+
   // Create a simple object graph with objects a, b, c, d:
   //  a+->b+->c
   //  +   +
@@ -117,7 +118,7 @@
       HANDLESCOPE(thread);
       Array& path = Array::Handle(Array::New(6, Heap::kNew));
       // Trigger a full GC to increase probability of concurrent tasks.
-      isolate->heap()->CollectAllGarbage();
+      heap->CollectAllGarbage();
       intptr_t length = graph.RetainingPath(&c, path).length;
       EXPECT_LE(3, length);
       Array& expected_c = Array::Handle();
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 459b279..9eaf367 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -98,7 +98,7 @@
   jsobj.AddProperty("_finalized", is_finalized());
   jsobj.AddProperty("_implemented", is_implemented());
   jsobj.AddProperty("_patch", false);
-  jsobj.AddProperty("_traceAllocations", TraceAllocation(isolate));
+  jsobj.AddProperty("_traceAllocations", TraceAllocation(isolate->group()));
 
   const Class& superClass = Class::Handle(SuperClass());
   if (!superClass.IsNull()) {
@@ -174,8 +174,7 @@
   // preserved when the table grows and the entries get rehashed. Use the ring.
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
-  ObjectStore* object_store = isolate->object_store();
+  auto object_store = thread->isolate_group()->object_store();
   CanonicalTypeArgumentsSet typeargs_table(
       zone, object_store->canonical_type_arguments());
   const Array& table =
@@ -401,7 +400,7 @@
   } else if (guarded_cid() == kDynamicCid) {
     jsobj.AddProperty("_guardClass", "dynamic");
   } else {
-    ClassTable* table = Isolate::Current()->class_table();
+    ClassTable* table = IsolateGroup::Current()->class_table();
     ASSERT(table->IsValidIndex(guarded_cid()));
     cls = table->At(guarded_cid());
     jsobj.AddProperty("_guardClass", cls);
@@ -802,7 +801,7 @@
 
 void ICData::PrintToJSONArray(const JSONArray& jsarray,
                               TokenPosition token_pos) const {
-  Isolate* isolate = Isolate::Current();
+  auto class_table = IsolateGroup::Current()->class_table();
   Class& cls = Class::Handle();
   Function& func = Function::Handle();
 
@@ -819,7 +818,7 @@
     intptr_t count = GetCountAt(i);
     if (!is_static_call()) {
       intptr_t cid = GetReceiverClassIdAt(i);
-      cls = isolate->class_table()->At(cid);
+      cls = class_table->At(cid);
       cache_entry.AddProperty("receiver", cls);
     }
     cache_entry.AddProperty("target", func);
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index 143a7bb..088c5ed 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -144,12 +144,9 @@
 ErrorPtr ObjectStore::PreallocateObjects() {
   Thread* thread = Thread::Current();
   IsolateGroup* isolate_group = thread->isolate_group();
-  Isolate* isolate = thread->isolate();
   // Either we are the object store on isolate group, or isolate group has no
   // object store and we are the object store on the isolate.
-  ASSERT(isolate_group != NULL && (isolate_group->object_store() == this ||
-                                   (isolate_group->object_store() == nullptr &&
-                                    isolate->object_store() == this)));
+  ASSERT(isolate_group != nullptr && isolate_group->object_store() == this);
 
   if (this->stack_overflow() != Instance::null()) {
     ASSERT(this->out_of_memory() != Instance::null());
@@ -204,8 +201,8 @@
   // The rest of these objects are only needed for code generation.
   return;
 #else
-  Isolate* isolate = thread->isolate();
-  ASSERT(isolate != NULL && isolate->object_store() == this);
+  auto isolate_group = thread->isolate_group();
+  ASSERT(isolate_group != nullptr && isolate_group->object_store() == this);
 
   const Library& async_lib = Library::Handle(zone, async_library());
   ASSERT(!async_lib.IsNull());
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index c332d17..fcd3423 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -161,8 +161,8 @@
 
 ISOLATE_UNIT_TEST_CASE(SixtyThousandDartClasses) {
   auto zone = thread->zone();
-  auto isolate = thread->isolate();
-  auto class_table = isolate->class_table();
+  auto isolate_group = thread->isolate_group();
+  auto class_table = isolate_group->class_table();
 
   const intptr_t start_cid = class_table->NumCids();
   const intptr_t num_classes = std::numeric_limits<uint16_t>::max() - start_cid;
@@ -223,7 +223,7 @@
   EXPECT_EQ((1 << 16) - 1, class_table->NumCids());
 
   // Ensure GC runs and can recognize all those new instances.
-  isolate->heap()->CollectAllGarbage();
+  isolate_group->heap()->CollectAllGarbage();
 
   // Ensure the instances are what we expect.
   for (intptr_t i = 0; i < num_classes; ++i) {
@@ -2225,7 +2225,7 @@
     value = Smi::New(i);
     array.Add(value);
   }
-  Heap* heap = Isolate::Current()->heap();
+  Heap* heap = IsolateGroup::Current()->heap();
   GCTestHelper::CollectAllGarbage();
   intptr_t capacity_before = heap->CapacityInWords(Heap::kOld);
   new_array = Array::MakeFixedLength(array);
@@ -2660,7 +2660,7 @@
   EXPECT_STREQ("-15", Smi::Handle(Smi::New(-15)).ToCString());
 
   // bool class and true/false values.
-  ObjectStore* object_store = Isolate::Current()->object_store();
+  ObjectStore* object_store = IsolateGroup::Current()->object_store();
   const Class& bool_class = Class::Handle(object_store->bool_class());
   EXPECT_STREQ("Library:'dart:core' Class: bool", bool_class.ToCString());
   EXPECT_STREQ("true", Bool::True().ToCString());
@@ -4251,7 +4251,7 @@
 }
 
 ISOLATE_UNIT_TEST_CASE(SpecialClassesHaveEmptyArrays) {
-  ObjectStore* object_store = Isolate::Current()->object_store();
+  ObjectStore* object_store = IsolateGroup::Current()->object_store();
   Class& cls = Class::Handle();
   Object& array = Object::Handle();
 
@@ -4360,7 +4360,7 @@
   // Class reference
   {
     JSONStream js;
-    Class& cls = Class::Handle(isolate->object_store()->bool_class());
+    Class& cls = Class::Handle(isolate->group()->object_store()->bool_class());
     cls.PrintJSON(&js, true);
     ElideJSONSubstring("classes", js.ToCString(), buffer);
     EXPECT_STREQ(
@@ -4371,7 +4371,7 @@
   {
     Thread* thread = Thread::Current();
     JSONStream js;
-    Class& cls = Class::Handle(isolate->object_store()->bool_class());
+    Class& cls = Class::Handle(isolate->group()->object_store()->bool_class());
     const String& func_name = String::Handle(String::New("toString"));
     Function& func =
         Function::Handle(Resolver::ResolveFunction(Z, cls, func_name));
@@ -4391,7 +4391,8 @@
   // Library reference
   {
     JSONStream js;
-    Library& lib = Library::Handle(isolate->object_store()->core_library());
+    Library& lib =
+        Library::Handle(isolate->group()->object_store()->core_library());
     lib.PrintJSON(&js, true);
     ElideJSONSubstring("libraries", js.ToCString(), buffer);
     EXPECT_STREQ(
@@ -4559,7 +4560,8 @@
   // TODO(turnidge): Add in all of the other Type siblings.
   {
     JSONStream js;
-    Instance& type = Instance::Handle(isolate->object_store()->bool_type());
+    Instance& type =
+        Instance::Handle(isolate->group()->object_store()->bool_type());
     type.PrintJSON(&js, true);
     ElideJSONSubstring("classes", js.ToCString(), buffer);
     ElideJSONSubstring("objects", buffer, buffer);
@@ -4665,73 +4667,6 @@
   EXPECT(result.IsIdenticalTo(expected));
 }
 
-static void CheckIdenticalHashStructure(Thread* T,
-                                        const Instance& a,
-                                        const Instance& b) {
-  const char* kScript =
-      "(a, b) {\n"
-      "  if (a._usedData != b._usedData ||\n"
-      "      a._deletedKeys != b._deletedKeys ||\n"
-      "      a._hashMask != b._hashMask ||\n"
-      "      a._index.length != b._index.length ||\n"
-      "      a._data.length != b._data.length) {\n"
-      "    return false;\n"
-      "  }\n"
-      "  for (var i = 0; i < a._index.length; ++i) {\n"
-      "    if (a._index[i] != b._index[i]) {\n"
-      "      return false;\n"
-      "    }\n"
-      "  }\n"
-      "  for (var i = 0; i < a._data.length; ++i) {\n"
-      "    var ad = a._data[i];\n"
-      "    var bd = b._data[i];\n"
-      "    if (!identical(ad, bd) && !(ad == a && bd == b)) {\n"
-      "      return false;\n"
-      "    }\n"
-      "  }\n"
-      "  return true;\n"
-      "}(a, b)";
-  String& name = String::Handle();
-  Array& param_names = Array::Handle(Array::New(2));
-  name = String::New("a");
-  param_names.SetAt(0, name);
-  name = String::New("b");
-  param_names.SetAt(1, name);
-  Array& param_values = Array::Handle(Array::New(2));
-  param_values.SetAt(0, a);
-  param_values.SetAt(1, b);
-  name = String::New(kScript);
-  Library& lib = Library::Handle(Library::CollectionLibrary());
-  EXPECT(Api::UnwrapHandle(TestCase::EvaluateExpression(
-             lib, name, param_names, param_values)) == Bool::True().raw());
-}
-
-TEST_CASE(LinkedHashMap) {
-  // Check that initial index size and hash mask match in Dart vs. C++.
-  // 1. Create an empty custom linked hash map in Dart.
-  const char* kScript =
-      "import 'dart:collection';\n"
-      "makeMap() {\n"
-      "  bool Function(dynamic, dynamic) eq = (a, b) => true;\n"
-      "  int Function(dynamic) hc = (a) => 42;\n"
-      "  return new LinkedHashMap(equals: eq, hashCode: hc);\n"
-      "}";
-  Dart_Handle h_lib = TestCase::LoadTestScript(kScript, NULL);
-  EXPECT_VALID(h_lib);
-  Dart_Handle h_result = Dart_Invoke(h_lib, NewString("makeMap"), 0, NULL);
-  EXPECT_VALID(h_result);
-
-  TransitionNativeToVM transition(thread);
-
-  // 2. Create an empty internalized LinkedHashMap in C++.
-  Instance& dart_map = Instance::Handle();
-  dart_map ^= Api::UnwrapHandle(h_result);
-  LinkedHashMap& cc_map = LinkedHashMap::Handle(LinkedHashMap::NewDefault());
-
-  // 3. Expect them to have identical structure.
-  CheckIdenticalHashStructure(thread, dart_map, cc_map);
-}
-
 TEST_CASE(LinkedHashMap_iteration) {
   const char* kScript =
       "makeMap() {\n"
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc
index d539a35..8df86d6 100644
--- a/runtime/vm/profiler_service.cc
+++ b/runtime/vm/profiler_service.cc
@@ -1406,8 +1406,8 @@
   }
 
   bool IsPCInDartHeap(uword pc) {
-    return vm_isolate_->heap()->CodeContains(pc) ||
-           thread_->isolate()->heap()->CodeContains(pc);
+    return vm_isolate_->group()->heap()->CodeContains(pc) ||
+           thread_->isolate()->group()->heap()->CodeContains(pc);
   }
 
   ProfileCode* FindOrRegisterNativeProfileCode(uword pc) {
diff --git a/runtime/vm/profiler_test.cc b/runtime/vm/profiler_test.cc
index 612e81a..49ecdd3 100644
--- a/runtime/vm/profiler_test.cc
+++ b/runtime/vm/profiler_test.cc
@@ -833,7 +833,7 @@
   Isolate* isolate = thread->isolate();
 
   const Class& double_class =
-      Class::Handle(isolate->object_store()->double_class());
+      Class::Handle(isolate->group()->object_store()->double_class());
   EXPECT(!double_class.IsNull());
 
   Dart_Handle args[2];
@@ -902,7 +902,7 @@
   Isolate* isolate = thread->isolate();
 
   const Class& array_class =
-      Class::Handle(isolate->object_store()->array_class());
+      Class::Handle(isolate->group()->object_store()->array_class());
   EXPECT(!array_class.IsNull());
 
   Invoke(root_library, "foo");
@@ -1060,7 +1060,7 @@
   Isolate* isolate = thread->isolate();
 
   const Class& closure_class =
-      Class::Handle(Isolate::Current()->object_store()->closure_class());
+      Class::Handle(IsolateGroup::Current()->object_store()->closure_class());
   EXPECT(!closure_class.IsNull());
   closure_class.SetTraceAllocation(true);
 
@@ -1118,7 +1118,7 @@
   Isolate* isolate = thread->isolate();
 
   const Library& typed_data_library =
-      Library::Handle(isolate->object_store()->typed_data_library());
+      Library::Handle(isolate->group()->object_store()->typed_data_library());
 
   const Class& float32_list_class =
       Class::Handle(GetClass(typed_data_library, "_Float32List"));
@@ -1194,7 +1194,7 @@
   Isolate* isolate = thread->isolate();
 
   const Class& one_byte_string_class =
-      Class::Handle(isolate->object_store()->one_byte_string_class());
+      Class::Handle(isolate->group()->object_store()->one_byte_string_class());
   EXPECT(!one_byte_string_class.IsNull());
 
   Dart_Handle args[2];
@@ -1272,7 +1272,7 @@
   Isolate* isolate = thread->isolate();
 
   const Class& one_byte_string_class =
-      Class::Handle(isolate->object_store()->one_byte_string_class());
+      Class::Handle(isolate->group()->object_store()->one_byte_string_class());
   EXPECT(!one_byte_string_class.IsNull());
 
   Dart_Handle args[2];
diff --git a/runtime/vm/program_visitor.cc b/runtime/vm/program_visitor.cc
index 5b7319c..39ea254 100644
--- a/runtime/vm/program_visitor.cc
+++ b/runtime/vm/program_visitor.cc
@@ -209,10 +209,10 @@
 };
 
 void ProgramVisitor::WalkProgram(Zone* zone,
-                                 Isolate* isolate,
+                                 IsolateGroup* isolate_group,
                                  ClassVisitor* visitor) {
-  auto const object_store = isolate->object_store();
-  auto const heap = isolate->heap();
+  auto const object_store = isolate_group->object_store();
+  auto const heap = isolate_group->heap();
   ProgramWalker walker(zone, heap, visitor);
 
   // Walk through the libraries and patches, looking for visitable objects.
@@ -346,7 +346,7 @@
   DirectChainedHashMap<S> canonical_objects_;
 };
 
-void ProgramVisitor::BindStaticCalls(Zone* zone, Isolate* isolate) {
+void ProgramVisitor::BindStaticCalls(Zone* zone, IsolateGroup* isolate_group) {
   class BindStaticCallsVisitor : public CodeVisitor {
    public:
     explicit BindStaticCallsVisitor(Zone* zone)
@@ -423,15 +423,16 @@
   };
 
   BindStaticCallsVisitor visitor(zone);
-  WalkProgram(zone, isolate, &visitor);
+  WalkProgram(zone, isolate_group, &visitor);
 }
 
 DECLARE_FLAG(charp, trace_precompiler_to);
 DECLARE_FLAG(charp, write_v8_snapshot_profile_to);
 
-void ProgramVisitor::ShareMegamorphicBuckets(Zone* zone, Isolate* isolate) {
+void ProgramVisitor::ShareMegamorphicBuckets(Zone* zone,
+                                             IsolateGroup* isolate_group) {
   const GrowableObjectArray& table = GrowableObjectArray::Handle(
-      zone, isolate->object_store()->megamorphic_cache_table());
+      zone, isolate_group->object_store()->megamorphic_cache_table());
   if (table.IsNull()) return;
   MegamorphicCache& cache = MegamorphicCache::Handle(zone);
 
@@ -561,8 +562,9 @@
 
 typedef DirectChainedHashMap<StackMapEntryKeyIntValueTrait> StackMapEntryIntMap;
 
-void ProgramVisitor::NormalizeAndDedupCompressedStackMaps(Zone* zone,
-                                                          Isolate* isolate) {
+void ProgramVisitor::NormalizeAndDedupCompressedStackMaps(
+    Zone* zone,
+    IsolateGroup* isolate_group) {
   // Walks all the CSMs in Code objects and collects their entry information
   // for consolidation.
   class CollectStackMapEntriesVisitor : public CodeVisitor {
@@ -646,11 +648,13 @@
         public Dedupper<CompressedStackMaps,
                         PointerKeyValueTrait<const CompressedStackMaps>> {
    public:
-    NormalizeAndDedupCompressedStackMapsVisitor(Zone* zone, Isolate* isolate)
+    NormalizeAndDedupCompressedStackMapsVisitor(Zone* zone,
+                                                IsolateGroup* isolate_group)
         : Dedupper(zone),
           old_global_table_(CompressedStackMaps::Handle(
               zone,
-              isolate->object_store()->canonicalized_stack_map_entries())),
+              isolate_group->object_store()
+                  ->canonicalized_stack_map_entries())),
           entry_offsets_(zone),
           maps_(CompressedStackMaps::Handle(zone)) {
       ASSERT(old_global_table_.IsNull() || old_global_table_.IsGlobalTable());
@@ -661,7 +665,7 @@
       // frequency the same entry info was seen across all CSMs in each SME.
 
       CollectStackMapEntriesVisitor collect_visitor(zone, old_global_table_);
-      WalkProgram(zone, isolate, &collect_visitor);
+      WalkProgram(zone, isolate_group, &collect_visitor);
 
       // The results of phase 1 are used to create a new global table with
       // entries sorted by decreasing frequency, so that entries that appear
@@ -671,7 +675,7 @@
 
       const auto& new_global_table = CompressedStackMaps::Handle(
           zone, collect_visitor.CreateGlobalTable(&entry_offsets_));
-      isolate->object_store()->set_canonicalized_stack_map_entries(
+      isolate_group->object_store()->set_canonicalized_stack_map_entries(
           new_global_table);
 
       // 2) Visit all CSMs and replace each with a canonicalized normalized
@@ -720,8 +724,9 @@
     CompressedStackMaps& maps_;
   };
 
-  NormalizeAndDedupCompressedStackMapsVisitor dedup_visitor(zone, isolate);
-  WalkProgram(zone, isolate, &dedup_visitor);
+  NormalizeAndDedupCompressedStackMapsVisitor dedup_visitor(zone,
+                                                            isolate_group);
+  WalkProgram(zone, isolate_group, &dedup_visitor);
 }
 
 class PcDescriptorsKeyValueTrait {
@@ -742,7 +747,8 @@
   }
 };
 
-void ProgramVisitor::DedupPcDescriptors(Zone* zone, Isolate* isolate) {
+void ProgramVisitor::DedupPcDescriptors(Zone* zone,
+                                        IsolateGroup* isolate_group) {
   class DedupPcDescriptorsVisitor
       : public CodeVisitor,
         public Dedupper<PcDescriptors, PcDescriptorsKeyValueTrait> {
@@ -767,7 +773,7 @@
   };
 
   DedupPcDescriptorsVisitor visitor(zone);
-  WalkProgram(zone, isolate, &visitor);
+  WalkProgram(zone, isolate_group, &visitor);
 }
 
 class TypedDataKeyValueTrait {
@@ -796,7 +802,8 @@
   bool IsCorrectType(const Object& obj) const { return obj.IsTypedData(); }
 };
 
-void ProgramVisitor::DedupDeoptEntries(Zone* zone, Isolate* isolate) {
+void ProgramVisitor::DedupDeoptEntries(Zone* zone,
+                                       IsolateGroup* isolate_group) {
   class DedupDeoptEntriesVisitor : public CodeVisitor,
                                    public TypedDataDedupper {
    public:
@@ -831,11 +838,12 @@
 
   if (FLAG_precompiled_mode) return;
   DedupDeoptEntriesVisitor visitor(zone);
-  WalkProgram(zone, isolate, &visitor);
+  WalkProgram(zone, isolate_group, &visitor);
 }
 
 #if defined(DART_PRECOMPILER)
-void ProgramVisitor::DedupCatchEntryMovesMaps(Zone* zone, Isolate* isolate) {
+void ProgramVisitor::DedupCatchEntryMovesMaps(Zone* zone,
+                                              IsolateGroup* isolate_group) {
   class DedupCatchEntryMovesMapsVisitor : public CodeVisitor,
                                           public TypedDataDedupper {
    public:
@@ -855,7 +863,7 @@
 
   if (!FLAG_precompiled_mode) return;
   DedupCatchEntryMovesMapsVisitor visitor(zone);
-  WalkProgram(zone, isolate, &visitor);
+  WalkProgram(zone, isolate_group, &visitor);
 }
 
 class UnlinkedCallKeyValueTrait {
@@ -876,17 +884,18 @@
   }
 };
 
-void ProgramVisitor::DedupUnlinkedCalls(Zone* zone, Isolate* isolate) {
+void ProgramVisitor::DedupUnlinkedCalls(Zone* zone,
+                                        IsolateGroup* isolate_group) {
   class DedupUnlinkedCallsVisitor
       : public CodeVisitor,
         public Dedupper<UnlinkedCall, UnlinkedCallKeyValueTrait> {
    public:
-    explicit DedupUnlinkedCallsVisitor(Zone* zone, Isolate* isolate)
+    explicit DedupUnlinkedCallsVisitor(Zone* zone, IsolateGroup* isolate_group)
         : Dedupper(zone),
           entry_(Object::Handle(zone)),
           pool_(ObjectPool::Handle(zone)) {
       auto& gop = ObjectPool::Handle(
-          zone, isolate->object_store()->global_object_pool());
+          zone, isolate_group->object_store()->global_object_pool());
       ASSERT_EQUAL(!gop.IsNull(), FLAG_use_bare_instructions);
       DedupPool(gop);
     }
@@ -916,7 +925,7 @@
 
   if (!FLAG_precompiled_mode) return;
 
-  DedupUnlinkedCallsVisitor deduper(zone, isolate);
+  DedupUnlinkedCallsVisitor deduper(zone, isolate_group);
 
   // Note: in bare instructions mode we can still have object pools attached
   // to code objects and these pools need to be deduplicated.
@@ -927,7 +936,7 @@
   if (!FLAG_use_bare_instructions ||
       FLAG_write_v8_snapshot_profile_to != nullptr ||
       FLAG_trace_precompiler_to != nullptr) {
-    WalkProgram(zone, isolate, &deduper);
+    WalkProgram(zone, isolate_group, &deduper);
   }
 }
 #endif  // defined(DART_PRECOMPILER)
@@ -954,7 +963,8 @@
   }
 };
 
-void ProgramVisitor::DedupCodeSourceMaps(Zone* zone, Isolate* isolate) {
+void ProgramVisitor::DedupCodeSourceMaps(Zone* zone,
+                                         IsolateGroup* isolate_group) {
   class DedupCodeSourceMapsVisitor
       : public CodeVisitor,
         public Dedupper<CodeSourceMap, CodeSourceMapKeyValueTrait> {
@@ -978,7 +988,7 @@
   };
 
   DedupCodeSourceMapsVisitor visitor(zone);
-  WalkProgram(zone, isolate, &visitor);
+  WalkProgram(zone, isolate_group, &visitor);
 }
 
 class ArrayKeyValueTrait {
@@ -1007,7 +1017,7 @@
   }
 };
 
-void ProgramVisitor::DedupLists(Zone* zone, Isolate* isolate) {
+void ProgramVisitor::DedupLists(Zone* zone, IsolateGroup* isolate_group) {
   class DedupListsVisitor : public CodeVisitor,
                             public Dedupper<Array, ArrayKeyValueTrait> {
    public:
@@ -1083,7 +1093,7 @@
   };
 
   DedupListsVisitor visitor(zone);
-  WalkProgram(zone, isolate, &visitor);
+  WalkProgram(zone, isolate_group, &visitor);
 }
 
 // Traits for comparing two [Instructions] objects for equality, which is
@@ -1168,7 +1178,8 @@
 };
 #endif
 
-void ProgramVisitor::DedupInstructions(Zone* zone, Isolate* isolate) {
+void ProgramVisitor::DedupInstructions(Zone* zone,
+                                       IsolateGroup* isolate_group) {
   class DedupInstructionsVisitor
       : public CodeVisitor,
         public Dedupper<Instructions, InstructionsKeyValueTrait>,
@@ -1180,7 +1191,7 @@
           instructions_(Instructions::Handle(zone)) {
       if (Snapshot::IncludesCode(Dart::vm_snapshot_kind())) {
         // Prefer existing objects in the VM isolate.
-        Dart::vm_isolate()->heap()->VisitObjectsImagePages(this);
+        Dart::vm_isolate_group()->heap()->VisitObjectsImagePages(this);
       }
     }
 
@@ -1257,33 +1268,33 @@
 
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
     DedupInstructionsWithSameMetadataVisitor visitor(zone);
-    return WalkProgram(zone, isolate, &visitor);
+    return WalkProgram(zone, isolate_group, &visitor);
   }
 #endif  // defined(DART_PRECOMPILER)
 
   DedupInstructionsVisitor visitor(zone);
-  WalkProgram(zone, isolate, &visitor);
+  WalkProgram(zone, isolate_group, &visitor);
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
 void ProgramVisitor::Dedup(Thread* thread) {
 #if !defined(DART_PRECOMPILED_RUNTIME)
-  auto const isolate = thread->isolate();
+  auto const isolate_group = thread->isolate_group();
   StackZone stack_zone(thread);
   HANDLESCOPE(thread);
   auto const zone = thread->zone();
 
-  BindStaticCalls(zone, isolate);
-  ShareMegamorphicBuckets(zone, isolate);
-  NormalizeAndDedupCompressedStackMaps(zone, isolate);
-  DedupPcDescriptors(zone, isolate);
-  DedupDeoptEntries(zone, isolate);
+  BindStaticCalls(zone, isolate_group);
+  ShareMegamorphicBuckets(zone, isolate_group);
+  NormalizeAndDedupCompressedStackMaps(zone, isolate_group);
+  DedupPcDescriptors(zone, isolate_group);
+  DedupDeoptEntries(zone, isolate_group);
 #if defined(DART_PRECOMPILER)
-  DedupCatchEntryMovesMaps(zone, isolate);
-  DedupUnlinkedCalls(zone, isolate);
+  DedupCatchEntryMovesMaps(zone, isolate_group);
+  DedupUnlinkedCalls(zone, isolate_group);
 #endif
-  DedupCodeSourceMaps(zone, isolate);
-  DedupLists(zone, isolate);
+  DedupCodeSourceMaps(zone, isolate_group);
+  DedupLists(zone, isolate_group);
 
   // Reduces binary size but obfuscates profiler results.
   if (FLAG_dedup_instructions) {
@@ -1306,7 +1317,7 @@
     if (FLAG_precompiled_mode && !FLAG_use_bare_instructions) return;
 #endif
 
-    DedupInstructions(zone, isolate);
+    DedupInstructions(zone, isolate_group);
   }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 }
@@ -1386,7 +1397,7 @@
   }
 
   // Isolate stubs.
-  ObjectStore* object_store = thread->isolate()->object_store();
+  ObjectStore* object_store = thread->isolate_group()->object_store();
   ObjectPtr* from = object_store->from();
   ObjectPtr* to = object_store->to_snapshot(Snapshot::kFullAOT);
   for (ObjectPtr* p = from; p <= to; p++) {
@@ -1399,7 +1410,7 @@
 
   // Function code / allocation stubs.
   AssignLoadingUnitsCodeVisitor visitor(zone);
-  WalkProgram(zone, thread->isolate(), &visitor);
+  WalkProgram(zone, thread->isolate_group(), &visitor);
 }
 
 class ProgramHashVisitor : public CodeVisitor {
@@ -1462,9 +1473,9 @@
   Zone* zone = thread->zone();
 
   ProgramHashVisitor visitor(zone);
-  WalkProgram(zone, thread->isolate(), &visitor);
+  WalkProgram(zone, thread->isolate_group(), &visitor);
   visitor.VisitPool(ObjectPool::Handle(
-      zone, thread->isolate()->object_store()->global_object_pool()));
+      zone, thread->isolate_group()->object_store()->global_object_pool()));
   return visitor.hash();
 }
 
diff --git a/runtime/vm/program_visitor.h b/runtime/vm/program_visitor.h
index d535f1f..06aa481 100644
--- a/runtime/vm/program_visitor.h
+++ b/runtime/vm/program_visitor.h
@@ -87,13 +87,15 @@
 };
 
 class Thread;
-class Isolate;
+class IsolateGroup;
 
 class ProgramVisitor : public AllStatic {
  public:
   // Walks all non-null class, function, and code objects in the program as
   // necessary for the given visitor.
-  static void WalkProgram(Zone* zone, Isolate* isolate, ClassVisitor* visitor);
+  static void WalkProgram(Zone* zone,
+                          IsolateGroup* isolate_group,
+                          ClassVisitor* visitor);
 
   static void Dedup(Thread* thread);
 #if defined(DART_PRECOMPILER)
@@ -103,19 +105,19 @@
 
  private:
 #if !defined(DART_PRECOMPILED_RUNTIME)
-  static void BindStaticCalls(Zone* zone, Isolate* isolate);
-  static void ShareMegamorphicBuckets(Zone* zone, Isolate* isolate);
+  static void BindStaticCalls(Zone* zone, IsolateGroup* isolate_group);
+  static void ShareMegamorphicBuckets(Zone* zone, IsolateGroup* isolate_group);
   static void NormalizeAndDedupCompressedStackMaps(Zone* zone,
-                                                   Isolate* isolate);
-  static void DedupPcDescriptors(Zone* zone, Isolate* isolate);
-  static void DedupDeoptEntries(Zone* zone, Isolate* isolate);
+                                                   IsolateGroup* isolate_group);
+  static void DedupPcDescriptors(Zone* zone, IsolateGroup* isolate_group);
+  static void DedupDeoptEntries(Zone* zone, IsolateGroup* isolate_group);
 #if defined(DART_PRECOMPILER)
-  static void DedupCatchEntryMovesMaps(Zone* zone, Isolate* isolate);
-  static void DedupUnlinkedCalls(Zone* zone, Isolate* isolate);
+  static void DedupCatchEntryMovesMaps(Zone* zone, IsolateGroup* isolate_group);
+  static void DedupUnlinkedCalls(Zone* zone, IsolateGroup* isolate_group);
 #endif
-  static void DedupCodeSourceMaps(Zone* zone, Isolate* isolate);
-  static void DedupLists(Zone* zone, Isolate* isolate);
-  static void DedupInstructions(Zone* zone, Isolate* isolate);
+  static void DedupCodeSourceMaps(Zone* zone, IsolateGroup* isolate_group);
+  static void DedupLists(Zone* zone, IsolateGroup* isolate_group);
+  static void DedupInstructions(Zone* zone, IsolateGroup* isolate_group);
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 };
 
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 64adf66..0bc2ddb 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -21,7 +21,7 @@
   // (see [Object::FinalizeVMIsolate]).
   if (!IsOldObject() || !IsMarked()) return false;
 
-  auto heap = Dart::vm_isolate()->heap();
+  auto heap = Dart::vm_isolate_group()->heap();
   ASSERT(heap->UsedInWords(Heap::kNew) == 0);
   return heap->old_space()->ContainsUnsafe(ToAddr(this));
 }
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 639d149..f492800 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -149,7 +149,7 @@
   // Lookup the type class.
   SmiPtr raw_type_class_id = Smi::RawCast(type_class_id());
   ClassPtr type_class =
-      writer->isolate()->class_table()->At(Smi::Value(raw_type_class_id));
+      writer->isolate_group()->class_table()->At(Smi::Value(raw_type_class_id));
 
   // Write out typeclass_is_in_fullsnapshot first as this will
   // help the reader decide on how to canonicalize the type object.
@@ -300,7 +300,7 @@
     ASSERT(parameterized_function() == Function::null());
     // Write out the parameterized class.
     ClassPtr param_class =
-        writer->isolate()->class_table()->At(parameterized_class_id_);
+        writer->isolate_group()->class_table()->At(parameterized_class_id_);
     writer->WriteObjectImpl(param_class, kAsReference);
   } else {
     ASSERT(parameterized_function() != Function::null());
@@ -367,8 +367,8 @@
       // Lookup the type class.
       TypePtr raw_type = Type::RawCast(types()[i]);
       SmiPtr raw_type_class_id = Smi::RawCast(raw_type->ptr()->type_class_id());
-      ClassPtr type_class =
-          writer->isolate()->class_table()->At(Smi::Value(raw_type_class_id));
+      ClassPtr type_class = writer->isolate_group()->class_table()->At(
+          Smi::Value(raw_type_class_id));
       if (!writer->AllowObjectsInDartLibrary(type_class->ptr()->library())) {
         writer->WriteVMIsolateObject(kDynamicType);
       } else {
@@ -1143,7 +1143,7 @@
   // Thus, the index will probably be allocated in new space (unless it's huge).
   // TODO(koda): Eagerly rehash here when no keys have user-defined '==', and
   // in particular, if/when (const) maps are needed in the VM isolate snapshot.
-  ASSERT(reader->isolate() != Dart::vm_isolate());
+  ASSERT(reader->isolate_group() != Dart::vm_isolate_group());
   map.SetHashMask(0);  // Prefer sentinel 0 over null for better type feedback.
 
   reader->EnqueueRehashingOfMap(map);
diff --git a/runtime/vm/regexp_assembler_bytecode.cc b/runtime/vm/regexp_assembler_bytecode.cc
index 6db75e3..db8a554 100644
--- a/runtime/vm/regexp_assembler_bytecode.cc
+++ b/runtime/vm/regexp_assembler_bytecode.cc
@@ -505,9 +505,9 @@
   }
   if (result == IrregexpInterpreter::RE_EXCEPTION) {
     Thread* thread = Thread::Current();
-    Isolate* isolate = thread->isolate();
+    auto isolate_group = thread->isolate_group();
     const Instance& exception =
-        Instance::Handle(isolate->object_store()->stack_overflow());
+        Instance::Handle(isolate_group->object_store()->stack_overflow());
     Exceptions::Throw(thread, exception);
     UNREACHABLE();
   }
diff --git a/runtime/vm/regexp_assembler_ir.cc b/runtime/vm/regexp_assembler_ir.cc
index 3c48384..8de3437 100644
--- a/runtime/vm/regexp_assembler_ir.cc
+++ b/runtime/vm/regexp_assembler_ir.cc
@@ -241,7 +241,7 @@
   TAG();
 
   Value* type = Bind(new (Z) ConstantInstr(TypeArguments::ZoneHandle(
-      Z, Isolate::Current()->object_store()->type_argument_int())));
+      Z, IsolateGroup::Current()->object_store()->type_argument_int())));
   Value* length = Bind(Uint64Constant(saved_registers_count_));
   Value* array = Bind(new (Z) CreateArrayInstr(InstructionSource(), type,
                                                length, GetNextDeoptId()));
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index bcf1991..cecf9b1 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -103,7 +103,7 @@
   Thread* thread = Thread::Current();
   TransitionGeneratedToVM transition(thread);
   VerifyPointersVisitor::VerifyPointers();
-  thread->isolate()->heap()->Verify();
+  thread->isolate_group()->heap()->Verify();
 }
 #endif
 
@@ -181,7 +181,7 @@
   const uword pc_offset = caller_frame->pc() - code.PayloadStart();
 
   if (FLAG_shared_slow_path_triggers_gc) {
-    isolate->heap()->CollectAllGarbage();
+    isolate->group()->heap()->CollectAllGarbage();
   }
 
   const CodeSourceMap& map =
@@ -261,7 +261,7 @@
   }
   if (len > Array::kMaxElements) {
     const Instance& exception = Instance::Handle(
-        zone, thread->isolate()->object_store()->out_of_memory());
+        zone, thread->isolate_group()->object_store()->out_of_memory());
     Exceptions::Throw(thread, exception);
   }
 
@@ -297,7 +297,7 @@
     Exceptions::ThrowRangeError("length", Integer::Cast(length), 0, max);
   } else if (len > max) {
     const Instance& exception = Instance::Handle(
-        zone, thread->isolate()->object_store()->out_of_memory());
+        zone, thread->isolate_group()->object_store()->out_of_memory());
     Exceptions::Throw(thread, exception);
   }
   const auto& typed_data =
@@ -1243,7 +1243,7 @@
                                           const ArgumentsDescriptor& desc) {
   const bool result = receiver.IsInstanceOf(type, Object::null_type_arguments(),
                                             Object::null_type_arguments());
-  const ObjectStore* store = Isolate::Current()->object_store();
+  const ObjectStore* store = IsolateGroup::Current()->object_store();
   const Function& target =
       Function::Handle(result ? store->simple_instance_of_true_function()
                               : store->simple_instance_of_false_function());
@@ -1267,8 +1267,9 @@
                                                                name, args_desc);
   }
   if (caller_arguments.length() == 2 &&
-      target_function.raw() ==
-          thread->isolate()->object_store()->simple_instance_of_function()) {
+      target_function.raw() == thread->isolate_group()
+                                   ->object_store()
+                                   ->simple_instance_of_function()) {
     // Replace the target function with constant function.
     const AbstractType& type = AbstractType::Cast(*caller_arguments[1]);
     target_function =
@@ -1340,14 +1341,14 @@
 
 #if defined(DART_PRECOMPILED_RUNTIME)
 
-static bool IsSingleTarget(Isolate* isolate,
+static bool IsSingleTarget(IsolateGroup* isolate_group,
                            Zone* zone,
                            intptr_t lower_cid,
                            intptr_t upper_cid,
                            const Function& target,
                            const String& name) {
   Class& cls = Class::Handle(zone);
-  ClassTable* table = isolate->class_table();
+  ClassTable* table = isolate_group->class_table();
   Function& other_target = Function::Handle(zone);
   for (intptr_t cid = lower_cid; cid <= upper_cid; cid++) {
     if (!table->HasValidClassAt(cid)) continue;
@@ -1363,7 +1364,6 @@
   return true;
 }
 
-
 class SavedUnlinkedCallMapKeyEqualsTraits : public AllStatic {
  public:
   static const char* Name() { return "SavedUnlinkedCallMapKeyEqualsTraits "; }
@@ -1631,8 +1631,8 @@
     *upper = receiver().GetClassId();
   }
 
-  return IsSingleTarget(isolate_, zone_, unchecked_lower, unchecked_upper,
-                        target_function, name);
+  return IsSingleTarget(isolate_->group(), zone_, unchecked_lower,
+                        unchecked_upper, target_function, name);
 }
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 
@@ -1668,8 +1668,8 @@
     old_expected_cid = MonomorphicSmiableCall::Cast(data).expected_cid();
   }
   const bool is_monomorphic_hit = old_expected_cid == receiver().GetClassId();
-  const auto& old_receiver_class =
-      Class::Handle(zone_, isolate_->class_table()->At(old_expected_cid));
+  const auto& old_receiver_class = Class::Handle(
+      zone_, isolate_->group()->class_table()->At(old_expected_cid));
   const auto& old_target = Function::Handle(
       zone_, Resolve(thread_, zone_, caller_arguments_, old_receiver_class,
                      name_, args_descriptor_));
@@ -2417,10 +2417,11 @@
 //  - garbage collection
 //  - hot reload
 static void HandleStackOverflowTestCases(Thread* thread) {
-  Isolate* isolate = thread->isolate();
+  auto isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
 
   if (FLAG_shared_slow_path_triggers_gc) {
-    isolate->heap()->CollectAllGarbage();
+    isolate->group()->heap()->CollectAllGarbage();
   }
 
   bool do_deopt = false;
@@ -2501,7 +2502,7 @@
     {
       NoReloadScope no_reload(isolate, thread);
       const Library& lib =
-          Library::Handle(isolate->object_store()->_internal_library());
+          Library::Handle(isolate_group->object_store()->_internal_library());
       const Class& cls = Class::Handle(
           lib.LookupClass(String::Handle(String::New("VMLibraryHooks"))));
       const Function& func = Function::Handle(Resolver::ResolveFunction(
@@ -2558,15 +2559,15 @@
     }
   }
   if (do_gc) {
-    isolate->heap()->CollectAllGarbage(Heap::kDebugging);
+    isolate->group()->heap()->CollectAllGarbage(Heap::kDebugging);
   }
 }
 #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
 static void HandleOSRRequest(Thread* thread) {
-  Isolate* isolate = thread->isolate();
-  ASSERT(isolate->use_osr());
+  auto isolate_group = thread->isolate_group();
+  ASSERT(isolate_group->use_osr());
   DartFrameIterator iterator(thread,
                              StackFrameIterator::kNoCrossThreadIteration);
   StackFrame* frame = iterator.NextFrame();
@@ -2622,7 +2623,7 @@
 
 DEFINE_RUNTIME_ENTRY(AllocateMint, 0) {
   if (FLAG_shared_slow_path_triggers_gc) {
-    isolate->heap()->CollectAllGarbage();
+    isolate->group()->heap()->CollectAllGarbage();
   }
   constexpr uint64_t val = 0x7fffffff7fffffff;
   ASSERT(!Smi::IsValid(static_cast<int64_t>(val)));
@@ -2673,7 +2674,7 @@
     // Use the preallocated stack overflow exception to avoid calling
     // into dart code.
     const Instance& exception =
-        Instance::Handle(isolate->object_store()->stack_overflow());
+        Instance::Handle(isolate->group()->object_store()->stack_overflow());
     Exceptions::Throw(thread, exception);
     UNREACHABLE();
   }
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 6118c2b..1856c56 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -412,7 +412,7 @@
 
 static bool IsValidClassId(Isolate* isolate, intptr_t cid) {
   ASSERT(isolate != NULL);
-  ClassTable* class_table = isolate->class_table();
+  ClassTable* class_table = isolate->group()->class_table();
   ASSERT(class_table != NULL);
   return class_table->IsValidIndex(cid) && class_table->HasValidClassAt(cid);
 }
@@ -420,7 +420,7 @@
 static ClassPtr GetClassForId(Isolate* isolate, intptr_t cid) {
   ASSERT(isolate == Isolate::Current());
   ASSERT(isolate != NULL);
-  ClassTable* class_table = isolate->class_table();
+  ClassTable* class_table = isolate->group()->class_table();
   ASSERT(class_table != NULL);
   return class_table->At(cid);
 }
@@ -1495,12 +1495,11 @@
 };
 
 static bool GetScripts(Thread* thread, JSONStream* js) {
-  Isolate* isolate = thread->isolate();
+  auto object_store = thread->isolate_group()->object_store();
   Zone* zone = thread->zone();
-  ASSERT(isolate != NULL);
 
-  const GrowableObjectArray& libs =
-      GrowableObjectArray::Handle(zone, isolate->object_store()->libraries());
+  const auto& libs =
+      GrowableObjectArray::Handle(zone, object_store->libraries());
   intptr_t num_libs = libs.Length();
 
   Library& lib = Library::Handle(zone);
@@ -1800,15 +1799,15 @@
   return Object::sentinel().raw();
 }
 
-static ObjectPtr LookupHeapObjectLibraries(Isolate* isolate,
+static ObjectPtr LookupHeapObjectLibraries(IsolateGroup* isolate_group,
                                            char** parts,
                                            int num_parts) {
   // Library ids look like "libraries/35"
   if (num_parts < 2) {
     return Object::sentinel().raw();
   }
-  const GrowableObjectArray& libs =
-      GrowableObjectArray::Handle(isolate->object_store()->libraries());
+  const auto& libs =
+      GrowableObjectArray::Handle(isolate_group->object_store()->libraries());
   ASSERT(!libs.IsNull());
   const String& id = String::Handle(String::New(parts[1]));
   // Scan for private key.
@@ -1894,9 +1893,8 @@
   if (num_parts < 2) {
     return Object::sentinel().raw();
   }
-  Isolate* isolate = thread->isolate();
   Zone* zone = thread->zone();
-  ClassTable* table = isolate->class_table();
+  auto table = thread->isolate_group()->class_table();
   intptr_t id;
   if (!GetIntegerId(parts[1], &id) || !table->IsValidIndex(id)) {
     return Object::sentinel().raw();
@@ -1945,7 +1943,6 @@
 static ObjectPtr LookupHeapObjectTypeArguments(Thread* thread,
                                                char** parts,
                                                int num_parts) {
-  Isolate* isolate = thread->isolate();
   // TypeArguments ids look like: "typearguments/17"
   if (num_parts < 2) {
     return Object::sentinel().raw();
@@ -1954,7 +1951,7 @@
   if (!GetIntegerId(parts[1], &id)) {
     return Object::sentinel().raw();
   }
-  ObjectStore* object_store = isolate->object_store();
+  ObjectStore* object_store = thread->isolate_group()->object_store();
   const Array& table =
       Array::Handle(thread->zone(), object_store->canonical_type_arguments());
   ASSERT(table.Length() > 0);
@@ -1965,9 +1962,7 @@
   return table.At(id);
 }
 
-static ObjectPtr LookupHeapObjectCode(Isolate* isolate,
-                                      char** parts,
-                                      int num_parts) {
+static ObjectPtr LookupHeapObjectCode(char** parts, int num_parts) {
   if (num_parts != 2) {
     return Object::sentinel().raw();
   }
@@ -2083,13 +2078,13 @@
     return obj.raw();
 
   } else if (strcmp(parts[0], "libraries") == 0) {
-    return LookupHeapObjectLibraries(isolate, parts, num_parts);
+    return LookupHeapObjectLibraries(isolate->group(), parts, num_parts);
   } else if (strcmp(parts[0], "classes") == 0) {
     return LookupHeapObjectClasses(thread, parts, num_parts);
   } else if (strcmp(parts[0], "typearguments") == 0) {
     return LookupHeapObjectTypeArguments(thread, parts, num_parts);
   } else if (strcmp(parts[0], "code") == 0) {
-    return LookupHeapObjectCode(isolate, parts, num_parts);
+    return LookupHeapObjectCode(parts, num_parts);
   } else if (strcmp(parts[0], "messages") == 0) {
     return LookupHeapObjectMessage(thread, parts, num_parts);
   }
@@ -4063,7 +4058,7 @@
   bool include_code_samples =
       BoolParameter::Parse(js->LookupParam("_code"), false);
 #if defined(DEBUG)
-  Isolate::Current()->heap()->CollectAllGarbage();
+  IsolateGroup::Current()->heap()->CollectAllGarbage();
 #endif
   if (CheckNativeAllocationProfilerDisabled(thread, js)) {
     return true;
@@ -4105,7 +4100,6 @@
       return true;
     }
   }
-  auto isolate = thread->isolate();
   auto isolate_group = thread->isolate_group();
   if (should_reset_accumulator) {
     isolate_group->UpdateLastAllocationProfileAccumulatorResetTimestamp();
@@ -4114,7 +4108,7 @@
     isolate_group->UpdateLastAllocationProfileGCTimestamp();
     isolate_group->heap()->CollectAllGarbage();
   }
-  isolate->class_table()->AllocationProfilePrintJSON(js, internal);
+  isolate_group->class_table()->AllocationProfilePrintJSON(js, internal);
   return true;
 }
 
@@ -4137,8 +4131,8 @@
 };
 
 static bool CollectAllGarbage(Thread* thread, JSONStream* js) {
-  Isolate* isolate = thread->isolate();
-  isolate->heap()->CollectAllGarbage(Heap::kDebugging);
+  auto heap = thread->isolate_group()->heap();
+  heap->CollectAllGarbage(Heap::kDebugging);
   PrintSuccess(js);
   return true;
 }
@@ -4149,20 +4143,21 @@
 };
 
 static bool GetHeapMap(Thread* thread, JSONStream* js) {
-  Isolate* isolate = thread->isolate();
+  auto isolate_group = thread->isolate_group();
   if (js->HasParam("gc")) {
     if (js->ParamIs("gc", "scavenge")) {
-      isolate->heap()->CollectGarbage(Heap::kScavenge, Heap::kDebugging);
+      isolate_group->heap()->CollectGarbage(Heap::kScavenge, Heap::kDebugging);
     } else if (js->ParamIs("gc", "mark-sweep")) {
-      isolate->heap()->CollectGarbage(Heap::kMarkSweep, Heap::kDebugging);
+      isolate_group->heap()->CollectGarbage(Heap::kMarkSweep, Heap::kDebugging);
     } else if (js->ParamIs("gc", "mark-compact")) {
-      isolate->heap()->CollectGarbage(Heap::kMarkCompact, Heap::kDebugging);
+      isolate_group->heap()->CollectGarbage(Heap::kMarkCompact,
+                                            Heap::kDebugging);
     } else {
       PrintInvalidParamError(js, "gc");
       return true;
     }
   }
-  isolate->heap()->PrintHeapMapToJSONStream(isolate, js);
+  isolate_group->heap()->PrintHeapMapToJSONStream(isolate_group, js);
   return true;
 }
 
@@ -4556,7 +4551,7 @@
 
 static bool GetObjectStore(Thread* thread, JSONStream* js) {
   JSONObject jsobj(js);
-  thread->isolate()->object_store()->PrintToJSONObject(&jsobj);
+  thread->isolate_group()->object_store()->PrintToJSONObject(&jsobj);
   return true;
 }
 
@@ -4577,7 +4572,7 @@
 };
 
 static bool GetClassList(Thread* thread, JSONStream* js) {
-  ClassTable* table = thread->isolate()->class_table();
+  ClassTable* table = thread->isolate_group()->class_table();
   JSONObject jsobj(js);
   table->PrintToJSONObject(&jsobj);
   return true;
@@ -4594,7 +4589,7 @@
     only_with_instantiations = true;
   }
   Zone* zone = thread->zone();
-  ObjectStore* object_store = thread->isolate()->object_store();
+  ObjectStore* object_store = thread->isolate_group()->object_store();
   CanonicalTypeArgumentsSet typeargs_table(
       zone, object_store->canonical_type_arguments());
   const intptr_t table_size = typeargs_table.NumEntries();
diff --git a/runtime/vm/service_event.cc b/runtime/vm/service_event.cc
index 145c9be..7f86141 100644
--- a/runtime/vm/service_event.cc
+++ b/runtime/vm/service_event.cc
@@ -252,8 +252,8 @@
   }
   if (gc_stats() != NULL) {
     jsobj.AddProperty("reason", Heap::GCReasonToString(gc_stats()->reason_));
-    isolate()->heap()->PrintToJSONObject(Heap::kNew, &jsobj);
-    isolate()->heap()->PrintToJSONObject(Heap::kOld, &jsobj);
+    isolate_group()->heap()->PrintToJSONObject(Heap::kNew, &jsobj);
+    isolate_group()->heap()->PrintToJSONObject(Heap::kOld, &jsobj);
   }
   if (bytes() != NULL) {
     jsobj.AddPropertyBase64("bytes", bytes(), bytes_length());
diff --git a/runtime/vm/service_event.h b/runtime/vm/service_event.h
index d02dbe2..735ab0f 100644
--- a/runtime/vm/service_event.h
+++ b/runtime/vm/service_event.h
@@ -14,6 +14,7 @@
 class Breakpoint;
 class Instance;
 class Isolate;
+class IsolateGroup;
 class Object;
 class StreamInfo;
 class String;
@@ -80,6 +81,7 @@
   ServiceEvent(Isolate* isolate, EventKind event_kind);
 
   Isolate* isolate() const { return isolate_; }
+  IsolateGroup* isolate_group() const { return isolate_->group(); }
 
   // Used by the C embedding api.
   Dart_Port isolate_id() const { return isolate_->main_port(); }
diff --git a/runtime/vm/service_isolate.cc b/runtime/vm/service_isolate.cc
index ab07c2a..6258f55 100644
--- a/runtime/vm/service_isolate.cc
+++ b/runtime/vm/service_isolate.cc
@@ -436,7 +436,7 @@
     HANDLESCOPE(T);
     // Invoke main which will set up the service port.
     const Library& root_library =
-        Library::Handle(Z, I->object_store()->root_library());
+        Library::Handle(Z, I->group()->object_store()->root_library());
     if (root_library.IsNull()) {
       if (FLAG_trace_service) {
         OS::PrintErr(DART_VM_SERVICE_ISOLATE_NAME
diff --git a/runtime/vm/simulator_arm.cc b/runtime/vm/simulator_arm.cc
index 67c7cbb..7eccbd8 100644
--- a/runtime/vm/simulator_arm.cc
+++ b/runtime/vm/simulator_arm.cc
@@ -307,7 +307,7 @@
   auto const Z = T->zone();
 #if defined(DART_PRECOMPILED_RUNTIME)
   auto const vm_instructions = reinterpret_cast<uword>(
-      Dart::vm_isolate()->group()->source()->snapshot_instructions);
+      Dart::vm_isolate_group()->source()->snapshot_instructions);
   auto const isolate_instructions = reinterpret_cast<uword>(
       T->isolate_group()->source()->snapshot_instructions);
   OS::PrintErr("vm_instructions=0x%" Px ", isolate_instructions=0x%" Px "\n",
@@ -538,7 +538,7 @@
           // Make the dereferencing '*' optional.
           if (((arg1[0] == '*') && GetValue(arg1 + 1, &value)) ||
               GetValue(arg1, &value)) {
-            if (Isolate::Current()->heap()->Contains(value)) {
+            if (IsolateGroup::Current()->heap()->Contains(value)) {
               OS::PrintErr("%s: \n", arg1);
 #if defined(DEBUG)
               const Object& obj = Object::Handle(static_cast<ObjectPtr>(value));
diff --git a/runtime/vm/simulator_arm64.cc b/runtime/vm/simulator_arm64.cc
index b39c371..1e3c5f6 100644
--- a/runtime/vm/simulator_arm64.cc
+++ b/runtime/vm/simulator_arm64.cc
@@ -334,7 +334,7 @@
   auto const Z = T->zone();
 #if defined(DART_PRECOMPILED_RUNTIME)
   auto const vm_instructions = reinterpret_cast<uword>(
-      Dart::vm_isolate()->group()->source()->snapshot_instructions);
+      Dart::vm_isolate_group()->source()->snapshot_instructions);
   auto const isolate_instructions = reinterpret_cast<uword>(
       T->isolate_group()->source()->snapshot_instructions);
   OS::PrintErr("vm_instructions=0x%" Px ", isolate_instructions=0x%" Px "\n",
@@ -595,7 +595,7 @@
           // Make the dereferencing '*' optional.
           if (((arg1[0] == '*') && GetValue(arg1 + 1, &value)) ||
               GetValue(arg1, &value)) {
-            if (Isolate::Current()->heap()->Contains(value)) {
+            if (IsolateGroup::Current()->heap()->Contains(value)) {
               OS::PrintErr("%s: \n", arg1);
 #if defined(DEBUG)
               const Object& obj = Object::Handle(static_cast<ObjectPtr>(value));
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 6655cbe..e9774c0 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -279,8 +279,8 @@
       kind_(kind),
       thread_(thread),
       zone_(thread->zone()),
-      heap_(isolate()->heap()),
-      old_space_(thread_->isolate()->heap()->old_space()),
+      heap_(isolate_group()->heap()),
+      old_space_(isolate_group()->heap()->old_space()),
       cls_(Class::Handle(zone_)),
       code_(Code::Handle(zone_)),
       instance_(Instance::Handle(zone_)),
@@ -302,7 +302,7 @@
       error_(UnhandledException::Handle(zone_)),
       set_class_(Class::ZoneHandle(
           zone_,
-          thread_->isolate()->object_store()->linked_hash_set_class())),
+          thread_->isolate_group()->object_store()->linked_hash_set_class())),
       max_vm_isolate_object_id_(
           (Snapshot::IsFull(kind))
               ? Object::vm_isolate_snapshot_object_table().Length()
@@ -506,7 +506,7 @@
 }
 
 bool SnapshotReader::is_vm_isolate() const {
-  return isolate() == Dart::vm_isolate();
+  return isolate_group() == Dart::vm_isolate_group();
 }
 
 ObjectPtr SnapshotReader::ReadObjectImpl(bool as_reference) {
@@ -646,7 +646,7 @@
     intptr_t result_cid = result->GetClassId();
 
     const auto unboxed_fields =
-        isolate()->group()->shared_class_table()->GetUnboxedFieldsMapAt(
+        isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt(
             result_cid);
 
     while (offset < next_field_offset) {
@@ -659,12 +659,13 @@
         pobj_ = ReadObjectImpl(read_as_reference);
         result->SetFieldAtOffset(offset, pobj_);
         if ((offset != type_argument_field_offset) &&
-            (kind_ == Snapshot::kMessage) && isolate()->use_field_guards() &&
+            (kind_ == Snapshot::kMessage) &&
+            isolate_group()->use_field_guards() &&
             (pobj_.raw() != Object::sentinel().raw())) {
           // TODO(fschneider): Consider hoisting these lookups out of the loop.
           // This would involve creating a handle, since cls_ can't be reused
           // across the call to ReadObjectImpl.
-          cls_ = isolate()->class_table()->At(result_cid);
+          cls_ = isolate_group()->class_table()->At(result_cid);
           array_ = cls_.OffsetToFieldMap();
           field_ ^= array_.At(offset >> kWordSizeLog2);
           ASSERT(!field_.IsNull());
@@ -839,7 +840,8 @@
   // Check it is a singleton class object.
   intptr_t class_id = ClassIdFromObjectId(object_id);
   if (IsSingletonClassId(class_id)) {
-    return isolate()->class_table()->At(class_id);  // get singleton class.
+    return isolate_group()->class_table()->At(
+        class_id);  // get singleton class.
   }
 
   // Check if it is a singleton Argument descriptor object.
@@ -863,7 +865,8 @@
 ObjectPtr SnapshotReader::ReadIndexedObject(intptr_t object_id) {
   intptr_t class_id = ClassIdFromObjectId(object_id);
   if (IsBootstrapedClassId(class_id)) {
-    return isolate()->class_table()->At(class_id);  // get singleton class.
+    return isolate_group()->class_table()->At(
+        class_id);  // get singleton class.
   }
   if (IsObjectStoreTypeId(object_id)) {
     return GetType(object_store(), object_id);  // return type obj.
@@ -911,8 +914,8 @@
     : BaseWriter(initial_size),
       thread_(thread),
       kind_(kind),
-      object_store_(isolate()->object_store()),
-      class_table_(isolate()->class_table()),
+      object_store_(isolate_group()->object_store()),
+      class_table_(isolate_group()->class_table()),
       forward_list_(forward_list),
       exception_type_(Exceptions::kNone),
       exception_msg_(NULL),
@@ -1493,7 +1496,7 @@
     WriteObjectImpl(cls, kAsInlinedObject);
 
     const auto unboxed_fields =
-        isolate()->group()->shared_class_table()->GetUnboxedFieldsMapAt(
+        isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt(
             cls->ptr()->id_);
 
     // Write out all the fields for the object.
diff --git a/runtime/vm/snapshot.h b/runtime/vm/snapshot.h
index 2d15658..27f02fd 100644
--- a/runtime/vm/snapshot.h
+++ b/runtime/vm/snapshot.h
@@ -258,9 +258,10 @@
   Thread* thread() const { return thread_; }
   Zone* zone() const { return zone_; }
   Isolate* isolate() const { return thread_->isolate(); }
+  IsolateGroup* isolate_group() const { return thread_->isolate_group(); }
   Heap* heap() const { return heap_; }
-  ObjectStore* object_store() const { return isolate()->object_store(); }
-  ClassTable* class_table() const { return isolate()->class_table(); }
+  ObjectStore* object_store() const { return isolate_group()->object_store(); }
+  ClassTable* class_table() const { return isolate_group()->class_table(); }
   PassiveObject* PassiveObjectHandle() { return &pobj_; }
   Array* ArrayHandle() { return &array_; }
   Class* ClassHandle() { return &cls_; }
@@ -590,7 +591,8 @@
   Thread* thread() const { return thread_; }
   Zone* zone() const { return thread_->zone(); }
   Isolate* isolate() const { return thread_->isolate(); }
-  Heap* heap() const { return isolate()->heap(); }
+  IsolateGroup* isolate_group() const { return thread_->isolate_group(); }
+  Heap* heap() const { return isolate_group()->heap(); }
 
   // Serialize an object into the buffer.
   void WriteObject(ObjectPtr raw);
diff --git a/runtime/vm/source_report.cc b/runtime/vm/source_report.cc
index d065499..5155d59 100644
--- a/runtime/vm/source_report.cc
+++ b/runtime/vm/source_report.cc
@@ -523,7 +523,7 @@
 
 void SourceReport::VisitClosures(JSONArray* jsarr) {
   const GrowableObjectArray& closures = GrowableObjectArray::Handle(
-      thread()->isolate()->object_store()->closure_functions());
+      thread()->isolate_group()->object_store()->closure_functions());
 
   // We need to keep rechecking the length of the closures array, as handling
   // a closure potentially adds new entries to the end.
@@ -546,7 +546,7 @@
     JSONArray ranges(&report, "ranges");
 
     const GrowableObjectArray& libs = GrowableObjectArray::Handle(
-        zone(), thread()->isolate()->object_store()->libraries());
+        zone(), thread()->isolate_group()->object_store()->libraries());
 
     // We only visit the libraries which actually load the specified script.
     Library& lib = Library::Handle(zone());
@@ -584,7 +584,7 @@
     GrowableArray<ScriptTableEntry*>* local_script_table_entries) {
   ScriptTableEntry wrapper;
   const GrowableObjectArray& libs = GrowableObjectArray::Handle(
-      zone(), thread()->isolate()->object_store()->libraries());
+      zone(), thread()->isolate_group()->object_store()->libraries());
   Library& lib = Library::Handle(zone());
   Script& scriptRef = Script::Handle(zone());
   for (int i = 0; i < libs.Length(); i++) {
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index 6d6f013..0f3c3ed 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -115,7 +115,7 @@
     ASSERT(cid == kNullCid || cid == kClassCid || cid == kFunctionCid);
     return cid == kFunctionCid;
   }
-  code = ReversePc::Lookup(Dart::vm_isolate()->group(), pc(),
+  code = ReversePc::Lookup(Dart::vm_isolate_group(), pc(),
                            /*is_return_address=*/true);
   if (!code.IsNull()) {
     auto const cid = code.OwnerClassId();
@@ -137,7 +137,7 @@
     ASSERT(cid == kNullCid || cid == kClassCid || cid == kFunctionCid);
     return cid == kNullCid || cid == kClassCid;
   }
-  code = ReversePc::Lookup(Dart::vm_isolate()->group(), pc(),
+  code = ReversePc::Lookup(Dart::vm_isolate_group(), pc(),
                            /*is_return_address=*/true);
   if (!code.IsNull()) {
     auto const cid = code.OwnerClassId();
@@ -248,15 +248,8 @@
     maps = code.compressed_stackmaps();
     CompressedStackMaps global_table;
 
-    // The GC does not have an active isolate, only an active isolate group,
-    // yet the global compressed stack map table is only stored in the object
-    // store. It has the same contents for all isolates, so we just pick the
-    // one from the first isolate here.
-    // TODO(dartbug.com/36097): Avoid having this per-isolate and instead store
-    // it per isolate group.
-    auto isolate = isolate_group()->isolates_.First();
-
-    global_table = isolate->object_store()->canonicalized_stack_map_entries();
+    global_table =
+        isolate_group()->object_store()->canonicalized_stack_map_entries();
     CompressedStackMaps::Iterator it(maps, global_table);
     const uword start = code.PayloadStart();
     const uint32_t pc_offset = pc() - start;
@@ -357,7 +350,7 @@
     if (code != Code::null()) {
       return code;
     }
-    code = ReversePc::Lookup(Dart::vm_isolate()->group(), pc(),
+    code = ReversePc::Lookup(Dart::vm_isolate_group(), pc(),
                              /*is_return_address=*/true);
     if (code != Code::null()) {
       return code;
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index 7f8f3a3..ebc7583 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -139,7 +139,7 @@
 
 CodePtr StubCode::GetAllocationStubForClass(const Class& cls) {
   Thread* thread = Thread::Current();
-  auto object_store = thread->isolate()->object_store();
+  auto object_store = thread->isolate_group()->object_store();
   Zone* zone = thread->zone();
   const Error& error =
       Error::Handle(zone, cls.EnsureIsAllocateFinalized(thread));
@@ -168,7 +168,7 @@
             : Code::PoolAttachment::kAttachPool;
 
     auto zone = thread->zone();
-    auto object_store = thread->isolate()->object_store();
+    auto object_store = thread->isolate_group()->object_store();
     auto& allocate_object_stub = Code::ZoneHandle(zone);
     auto& allocate_object_parametrized_stub = Code::ZoneHandle(zone);
     if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
@@ -242,7 +242,7 @@
 }
 
 CodePtr StubCode::GetAllocationStubForTypedData(classid_t class_id) {
-  auto object_store = Thread::Current()->isolate()->object_store();
+  auto object_store = Thread::Current()->isolate_group()->object_store();
   switch (class_id) {
     case kTypedDataInt8ArrayCid:
       return object_store->allocate_int8_array_stub();
@@ -283,7 +283,7 @@
 #if !defined(DART_PRECOMPILED_RUNTIME)
   auto thread = Thread::Current();
   auto Z = thread->zone();
-  auto object_store = thread->isolate()->object_store();
+  auto object_store = thread->isolate_group()->object_store();
 
   const auto& closure_class =
       Class::ZoneHandle(Z, object_store->closure_class());
@@ -340,7 +340,7 @@
     }
   }
 
-  auto object_store = Isolate::Current()->object_store();
+  auto object_store = IsolateGroup::Current()->object_store();
 
 #define MATCH(member, name)                                                    \
   if (object_store->member() != Code::null() &&                                \
diff --git a/runtime/vm/symbols.cc b/runtime/vm/symbols.cc
index fac122b..984d520 100644
--- a/runtime/vm/symbols.cc
+++ b/runtime/vm/symbols.cc
@@ -79,19 +79,20 @@
   return *symbol_handles_[token_id];
 }
 
-void Symbols::Init(Isolate* vm_isolate) {
+void Symbols::Init(IsolateGroup* vm_isolate_group) {
   // Should only be run by the vm isolate.
-  ASSERT(Isolate::Current() == Dart::vm_isolate());
-  ASSERT(vm_isolate == Dart::vm_isolate());
+  ASSERT(IsolateGroup::Current() == Dart::vm_isolate_group());
+  ASSERT(vm_isolate_group == Dart::vm_isolate_group());
   Zone* zone = Thread::Current()->zone();
 
   // Create and setup a symbol table in the vm isolate.
-  SetupSymbolTable(vm_isolate);
+  SetupSymbolTable(vm_isolate_group);
 
   // Create all predefined symbols.
   ASSERT((sizeof(names) / sizeof(const char*)) == Symbols::kNullCharId);
 
-  CanonicalStringSet table(zone, vm_isolate->object_store()->symbol_table());
+  CanonicalStringSet table(zone,
+                           vm_isolate_group->object_store()->symbol_table());
 
   // First set up all the predefined string symbols.
   // Create symbols for language keywords. Some keywords are equal to
@@ -122,16 +123,17 @@
     symbol_handles_[idx] = str;
   }
 
-  vm_isolate->object_store()->set_symbol_table(table.Release());
+  vm_isolate_group->object_store()->set_symbol_table(table.Release());
 }
 
-void Symbols::InitFromSnapshot(Isolate* vm_isolate) {
+void Symbols::InitFromSnapshot(IsolateGroup* vm_isolate_group) {
   // Should only be run by the vm isolate.
-  ASSERT(Isolate::Current() == Dart::vm_isolate());
-  ASSERT(vm_isolate == Dart::vm_isolate());
+  ASSERT(IsolateGroup::Current() == Dart::vm_isolate_group());
+  ASSERT(vm_isolate_group == Dart::vm_isolate_group());
   Zone* zone = Thread::Current()->zone();
 
-  CanonicalStringSet table(zone, vm_isolate->object_store()->symbol_table());
+  CanonicalStringSet table(zone,
+                           vm_isolate_group->object_store()->symbol_table());
 
   // Lookup all the predefined string symbols and language keyword symbols
   // and cache them in the read only handles for fast access.
@@ -162,24 +164,26 @@
     symbol_handles_[idx] = str;
   }
 
-  vm_isolate->object_store()->set_symbol_table(table.Release());
+  vm_isolate_group->object_store()->set_symbol_table(table.Release());
 }
 
-void Symbols::SetupSymbolTable(Isolate* isolate) {
-  ASSERT(isolate != NULL);
+void Symbols::SetupSymbolTable(IsolateGroup* isolate_group) {
+  ASSERT(isolate_group != nullptr);
 
   // Setup the symbol table used within the String class.
-  const intptr_t initial_size = (isolate == Dart::vm_isolate())
+  const intptr_t initial_size = (isolate_group == Dart::vm_isolate_group())
                                     ? kInitialVMIsolateSymtabSize
                                     : kInitialSymtabSize;
   Array& array = Array::Handle(
       HashTables::New<CanonicalStringSet>(initial_size, Heap::kOld));
-  isolate->object_store()->set_symbol_table(array);
+  isolate_group->object_store()->set_symbol_table(array);
 }
 
-void Symbols::GetStats(Isolate* isolate, intptr_t* size, intptr_t* capacity) {
-  ASSERT(isolate != NULL);
-  CanonicalStringSet table(isolate->object_store()->symbol_table());
+void Symbols::GetStats(IsolateGroup* isolate_group,
+                       intptr_t* size,
+                       intptr_t* capacity) {
+  ASSERT(isolate_group != nullptr);
+  CanonicalStringSet table(isolate_group->object_store()->symbol_table());
   *size = table.NumOccupied();
   *capacity = table.NumEntries();
   table.Release();
@@ -340,19 +344,15 @@
   Smi& value = thread->SmiHandle();
   Array& data = thread->ArrayHandle();
   {
-    Isolate* vm_isolate = Dart::vm_isolate();
-    data = vm_isolate->object_store()->symbol_table();
+    auto vm_isolate_group = Dart::vm_isolate_group();
+    data = vm_isolate_group->object_store()->symbol_table();
     CanonicalStringSet table(&key, &value, &data);
     symbol ^= table.GetOrNull(str);
     table.Release();
   }
   if (symbol.IsNull()) {
     IsolateGroup* group = thread->isolate_group();
-    Isolate* isolate = thread->isolate();
-    // In JIT object_store lives on isolate, not on isolate group.
-    ObjectStore* object_store = group->object_store() == nullptr
-                                    ? isolate->object_store()
-                                    : group->object_store();
+    ObjectStore* object_store = group->object_store();
     if (thread->IsAtSafepoint()) {
       // There are two cases where we can cause symbol allocation while holding
       // a safepoint:
@@ -424,19 +424,15 @@
   Smi& value = thread->SmiHandle();
   Array& data = thread->ArrayHandle();
   {
-    Isolate* vm_isolate = Dart::vm_isolate();
-    data = vm_isolate->object_store()->symbol_table();
+    auto vm_isolate_group = Dart::vm_isolate_group();
+    data = vm_isolate_group->object_store()->symbol_table();
     CanonicalStringSet table(&key, &value, &data);
     symbol ^= table.GetOrNull(str);
     table.Release();
   }
   if (symbol.IsNull()) {
     IsolateGroup* group = thread->isolate_group();
-    Isolate* isolate = thread->isolate();
-    // In JIT object_store lives on isolate, not on isolate group.
-    ObjectStore* object_store = group->object_store() == nullptr
-                                    ? isolate->object_store()
-                                    : group->object_store();
+    ObjectStore* object_store = group->object_store();
     // See `Symbols::NewSymbol` for more information why we separate the two
     // cases.
     if (thread->IsAtSafepoint()) {
@@ -532,24 +528,24 @@
   return predefined_[char_code];
 }
 
-void Symbols::DumpStats(Isolate* isolate) {
+void Symbols::DumpStats(IsolateGroup* isolate_group) {
   intptr_t size = -1;
   intptr_t capacity = -1;
   // First dump VM symbol table stats.
-  GetStats(Dart::vm_isolate(), &size, &capacity);
+  GetStats(Dart::vm_isolate_group(), &size, &capacity);
   OS::PrintErr("VM Isolate: Number of symbols : %" Pd "\n", size);
   OS::PrintErr("VM Isolate: Symbol table capacity : %" Pd "\n", capacity);
   // Now dump regular isolate symbol table stats.
-  GetStats(isolate, &size, &capacity);
+  GetStats(isolate_group, &size, &capacity);
   OS::PrintErr("Isolate: Number of symbols : %" Pd "\n", size);
   OS::PrintErr("Isolate: Symbol table capacity : %" Pd "\n", capacity);
   // TODO(koda): Consider recording growth and collision stats in HashTable,
   // in DEBUG mode.
 }
 
-void Symbols::DumpTable(Isolate* isolate) {
+void Symbols::DumpTable(IsolateGroup* isolate_group) {
   OS::PrintErr("symbols:\n");
-  CanonicalStringSet table(isolate->object_store()->symbol_table());
+  CanonicalStringSet table(isolate_group->object_store()->symbol_table());
   table.Dump();
   table.Release();
 }
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 2d21c6f..c84c323 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -12,7 +12,7 @@
 namespace dart {
 
 // Forward declarations.
-class Isolate;
+class IsolateGroup;
 class ObjectPointerVisitor;
 
 // One-character symbols are added implicitly.
@@ -641,11 +641,11 @@
   static const String& Token(Token::Kind token);
 
   // Initialize frequently used symbols in the vm isolate.
-  static void Init(Isolate* isolate);
-  static void InitFromSnapshot(Isolate* isolate);
+  static void Init(IsolateGroup* isolate_group);
+  static void InitFromSnapshot(IsolateGroup* isolate_group);
 
   // Initialize and setup a symbol table for the isolate.
-  static void SetupSymbolTable(Isolate* isolate);
+  static void SetupSymbolTable(IsolateGroup* isolate_group);
 
   // Creates a Symbol given a C string that is assumed to contain
   // UTF-8 encoded characters and '\0' is considered a termination character.
@@ -703,8 +703,8 @@
     return reinterpret_cast<StringPtr*>(&predefined_);
   }
 
-  static void DumpStats(Isolate* isolate);
-  static void DumpTable(Isolate* isolate);
+  static void DumpStats(IsolateGroup* isolate_group);
+  static void DumpTable(IsolateGroup* isolate_group);
 
   // Returns Symbol::Null if no symbol is found.
   template <typename StringType>
@@ -719,7 +719,9 @@
   static StringPtr LookupFromSet(Thread* thread, const String& str);
   static StringPtr LookupFromDot(Thread* thread, const String& str);
 
-  static void GetStats(Isolate* isolate, intptr_t* size, intptr_t* capacity);
+  static void GetStats(IsolateGroup* isolate_group,
+                       intptr_t* size,
+                       intptr_t* capacity);
 
  private:
   enum { kInitialVMIsolateSymtabSize = 1024, kInitialSymtabSize = 2048 };
diff --git a/runtime/vm/thread_test.cc b/runtime/vm/thread_test.cc
index 1640296..4e4d17f 100644
--- a/runtime/vm/thread_test.cc
+++ b/runtime/vm/thread_test.cc
@@ -295,7 +295,7 @@
   intptr_t done_count = 0;
   bool wait = true;
 
-  EXPECT(isolate->heap()->GrowthControlState());
+  EXPECT(isolate->group()->heap()->GrowthControlState());
 
   NoHeapGrowthControlScope no_heap_growth_scope;
 
@@ -843,7 +843,7 @@
       Zone* zone = stack_zone.GetZone();
       HANDLESCOPE(thread);
       String& old_str = String::Handle(zone, String::New("old", Heap::kOld));
-      isolate_->heap()->CollectAllGarbage();
+      isolate_->group()->heap()->CollectAllGarbage();
       EXPECT(old_str.Equals("old"));
     }
     Thread::ExitIsolateAsHelper();
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index 7964785..f9a7c85 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -37,7 +37,7 @@
   Zone* Z = Thread::Current()->zone();
   if (type.IsType() && !type.IsFunctionType()) {
     const intptr_t cid = Type::Cast(type).type_class_id();
-    ClassTable* class_table = Isolate::Current()->class_table();
+    ClassTable* class_table = IsolateGroup::Current()->class_table();
     klass_ = class_table->At(cid);
     ASSERT(!klass_.IsNull());
 
@@ -148,7 +148,7 @@
 #endif
 
 TypeTestingStubGenerator::TypeTestingStubGenerator()
-    : object_store_(Isolate::Current()->object_store()) {}
+    : object_store_(IsolateGroup::Current()->object_store()) {}
 
 CodePtr TypeTestingStubGenerator::OptimizedCodeForType(
     const AbstractType& type) {
@@ -205,7 +205,7 @@
 
   auto& slow_tts_stub = Code::ZoneHandle(zone);
   if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
-    slow_tts_stub = thread->isolate()->object_store()->slow_tts_stub();
+    slow_tts_stub = thread->isolate_group()->object_store()->slow_tts_stub();
   }
 
   // To use the already-defined __ Macro !
@@ -572,7 +572,7 @@
     }
     if (cid != kDynamicCid) {
       const Class& instance_klass =
-          Class::Handle(Isolate::Current()->class_table()->At(cid));
+          Class::Handle(IsolateGroup::Current()->class_table()->At(cid));
       if (load_field->slot().IsTypeArguments() && instance_klass.IsGeneric() &&
           compiler::target::Class::TypeArgumentsFieldOffset(instance_klass) ==
               load_field->slot().offset_in_bytes()) {
@@ -704,7 +704,8 @@
       finder_(zone_),
       assert_assignable_types_(),
       instance_creation_arguments_(
-          new TypeArgumentsSet[thread->isolate()->class_table()->NumCids()]),
+          new TypeArgumentsSet
+              [thread->isolate_group()->class_table()->NumCids()]),
       klass_(Class::Handle(zone_)) {
   thread->set_type_usage_info(this);
 }
@@ -759,7 +760,7 @@
 }
 
 void TypeUsageInfo::BuildTypeUsageInformation() {
-  ClassTable* class_table = thread()->isolate()->class_table();
+  ClassTable* class_table = thread()->isolate_group()->class_table();
   const intptr_t cid_count = class_table->NumCids();
 
   // Step 1) Propagate instantiated type argument vectors.
diff --git a/runtime/vm/type_testing_stubs_test.cc b/runtime/vm/type_testing_stubs_test.cc
index 6f7014d..e2d0017 100644
--- a/runtime/vm/type_testing_stubs_test.cc
+++ b/runtime/vm/type_testing_stubs_test.cc
@@ -166,7 +166,7 @@
 
   // Build a stub which will do calling conversion to call TTS stubs.
   const auto& klass =
-      Class::Handle(thread->isolate()->class_table()->At(kInstanceCid));
+      Class::Handle(thread->isolate_group()->class_table()->At(kInstanceCid));
   const auto& symbol = String::Handle(
       Symbols::New(thread, OS::SCreate(thread->zone(), "TTSTest")));
   const auto& function = Function::Handle(
diff --git a/sdk/lib/_internal/vm/lib/compact_hash.dart b/sdk/lib/_internal/vm/lib/compact_hash.dart
index 4574455..39c353f 100644
--- a/sdk/lib/_internal/vm/lib/compact_hash.dart
+++ b/sdk/lib/_internal/vm/lib/compact_hash.dart
@@ -24,13 +24,13 @@
   // least one unoccupied entry.
   // NOTE: When maps are deserialized, their _index and _hashMask is regenerated
   // eagerly by _regenerateIndex.
-  Uint32List _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE);
+  Uint32List _index = _initialIndex;
 
   // Cached in-place mask for the hash pattern component.
-  int _hashMask = _HashBase._indexSizeToHashMask(_HashBase._INITIAL_INDEX_SIZE);
+  int _hashMask = 0;
 
   // Fixed-length list of keys (set) or key/value at even/odd indices (map).
-  List _data;
+  List _data = _initialData;
 
   // Length of _data that is used (i.e., keys + values for a map).
   int _usedData = 0;
@@ -41,7 +41,7 @@
   // Note: All fields are initialized in a single constructor so that the VM
   // recognizes they cannot hold null values. This makes a big (20%) performance
   // difference on some operations.
-  _HashFieldBase(int dataSize) : this._data = new List.filled(dataSize, null);
+  _HashFieldBase(int dataSize);
 }
 
 // Base class for VM-internal classes; keep in sync with _HashFieldBase.
@@ -97,7 +97,7 @@
   // are doubled when _data is full. Thus, _index will have a max load factor
   // of 1/2, which enables one more bit to be used for the hash.
   // TODO(koda): Consider growing _data by factor sqrt(2), twice as often.
-  static const int _INITIAL_INDEX_BITS = 3;
+  static const int _INITIAL_INDEX_BITS = 2;
   static const int _INITIAL_INDEX_SIZE = 1 << (_INITIAL_INDEX_BITS + 1);
 
   // Unused and deleted entries are marked by 0 and 1, respectively.
@@ -155,6 +155,12 @@
   bool _equals(e1, e2) => identical(e1, e2);
 }
 
+final _initialIndex = new Uint32List(1);
+// Note: not const. Const arrays are made immutable by having a different class
+// than regular arrays that throws on element assignment. We want the data field
+// in maps and sets to be monomorphic.
+final _initialData = new List.filled(0, null);
+
 // VM-internalized implementation of a default-constructed LinkedHashMap.
 @pragma("vm:entry-point")
 class _InternalLinkedHashMap<K, V> extends _HashVMBase
@@ -165,9 +171,9 @@
         _OperatorEqualsAndHashCode
     implements LinkedHashMap<K, V> {
   _InternalLinkedHashMap() {
-    _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE);
-    _hashMask = _HashBase._indexSizeToHashMask(_HashBase._INITIAL_INDEX_SIZE);
-    _data = new List.filled(_HashBase._INITIAL_INDEX_SIZE, null);
+    _index = _initialIndex;
+    _hashMask = 0;
+    _data = _initialData;
     _usedData = 0;
     _deletedKeys = 0;
   }
@@ -202,6 +208,10 @@
 
   // Allocate new _index and _data, and optionally copy existing contents.
   void _init(int size, int hashMask, List? oldData, int oldUsed) {
+    if (size < _HashBase._INITIAL_INDEX_SIZE) {
+      size = _HashBase._INITIAL_INDEX_SIZE;
+      hashMask = _HashBase._indexSizeToHashMask(size);
+    }
     assert(size & (size - 1) == 0);
     assert(_HashBase._UNUSED_PAIR == 0);
     _index = new Uint32List(size);
@@ -222,7 +232,7 @@
 
   // This method is called by [_rehashObjects] (see above).
   void _regenerateIndex() {
-    _index = new Uint32List(_data.length);
+    _index = _data.length == 0 ? _initialIndex : new Uint32List(_data.length);
     assert(_hashMask == 0);
     _hashMask = _HashBase._indexSizeToHashMask(_index.length);
     final int tmpUsed = _usedData;
@@ -526,6 +536,10 @@
   }
 
   void _init(int size, int hashMask, List? oldData, int oldUsed) {
+    if (size < _HashBase._INITIAL_INDEX_SIZE) {
+      size = _HashBase._INITIAL_INDEX_SIZE;
+      hashMask = _HashBase._indexSizeToHashMask(size);
+    }
     _index = new Uint32List(size);
     _hashMask = hashMask;
     _data = new List.filled(size >> 1, null);
diff --git a/tools/VERSION b/tools/VERSION
index b988d50..baa01ff 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 196
+PRERELEASE 197
 PRERELEASE_PATCH 0
\ No newline at end of file