Merge "Simplify DexRegisterMap API."
diff --git a/libprofile/profile/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc
index ead7cba..42c3320 100644
--- a/libprofile/profile/profile_compilation_info_test.cc
+++ b/libprofile/profile/profile_compilation_info_test.cc
@@ -17,20 +17,14 @@
 #include <gtest/gtest.h>
 #include <stdio.h>
 
-#include "art_method-inl.h"
+#include "base/arena_allocator.h"
+#include "base/common_art_test.h"
 #include "base/unix_file/fd_file.h"
-#include "class_linker-inl.h"
-#include "common_runtime_test.h"
 #include "dex/dex_file.h"
 #include "dex/dex_file_loader.h"
 #include "dex/method_reference.h"
 #include "dex/type_reference.h"
-#include "handle_scope-inl.h"
-#include "linear_alloc.h"
-#include "mirror/class-inl.h"
-#include "mirror/class_loader.h"
 #include "profile/profile_compilation_info.h"
-#include "scoped_thread_state_change-inl.h"
 #include "ziparchive/zip_writer.h"
 
 namespace art {
@@ -39,31 +33,14 @@
 
 static constexpr size_t kMaxMethodIds = 65535;
 
-class ProfileCompilationInfoTest : public CommonRuntimeTest {
+class ProfileCompilationInfoTest : public CommonArtTest {
  public:
-  void PostRuntimeCreate() OVERRIDE {
-    allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
+  void SetUp() OVERRIDE {
+    CommonArtTest::SetUp();
+    allocator_.reset(new ArenaAllocator(&pool_));
   }
 
  protected:
-  std::vector<ArtMethod*> GetVirtualMethods(jobject class_loader,
-                                            const std::string& clazz) {
-    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-    Thread* self = Thread::Current();
-    ScopedObjectAccess soa(self);
-    StackHandleScope<1> hs(self);
-    Handle<mirror::ClassLoader> h_loader(
-        hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader()));
-    ObjPtr<mirror::Class> klass = class_linker->FindClass(self, clazz.c_str(), h_loader);
-
-    const auto pointer_size = class_linker->GetImagePointerSize();
-    std::vector<ArtMethod*> methods;
-    for (auto& m : klass->GetVirtualMethods(pointer_size)) {
-      methods.push_back(&m);
-    }
-    return methods;
-  }
-
   bool AddMethod(const std::string& dex_location,
                  uint32_t checksum,
                  uint16_t method_index,
@@ -97,89 +74,6 @@
     return static_cast<uint32_t>(file.GetFd());
   }
 
-  bool SaveProfilingInfo(
-      const std::string& filename,
-      const std::vector<ArtMethod*>& methods,
-      const std::set<DexCacheResolvedClasses>& resolved_classes,
-      Hotness::Flag flags) {
-    ProfileCompilationInfo info;
-    std::vector<ProfileMethodInfo> profile_methods;
-    ScopedObjectAccess soa(Thread::Current());
-    for (ArtMethod* method : methods) {
-      profile_methods.emplace_back(
-          MethodReference(method->GetDexFile(), method->GetDexMethodIndex()));
-    }
-    if (!info.AddMethods(profile_methods, flags) || !info.AddClasses(resolved_classes)) {
-      return false;
-    }
-    if (info.GetNumberOfMethods() != profile_methods.size()) {
-      return false;
-    }
-    ProfileCompilationInfo file_profile;
-    if (!file_profile.Load(filename, false)) {
-      return false;
-    }
-    if (!info.MergeWith(file_profile)) {
-      return false;
-    }
-
-    return info.Save(filename, nullptr);
-  }
-
-  // Saves the given art methods to a profile backed by 'filename' and adds
-  // some fake inline caches to it. The added inline caches are returned in
-  // the out map `profile_methods_map`.
-  bool SaveProfilingInfoWithFakeInlineCaches(
-      const std::string& filename,
-      const std::vector<ArtMethod*>& methods,
-      Hotness::Flag flags,
-      /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) {
-    ProfileCompilationInfo info;
-    std::vector<ProfileMethodInfo> profile_methods;
-    ScopedObjectAccess soa(Thread::Current());
-    for (ArtMethod* method : methods) {
-      std::vector<ProfileMethodInfo::ProfileInlineCache> caches;
-      // Monomorphic
-      for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
-        std::vector<TypeReference> classes;
-        classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0));
-        caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
-      }
-      // Polymorphic
-      for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
-        std::vector<TypeReference> classes;
-        for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) {
-          classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
-        }
-        caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
-      }
-      // Megamorphic
-      for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
-        std::vector<TypeReference> classes;
-        for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) {
-          classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
-        }
-        caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
-      }
-      // Missing types
-      for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
-        std::vector<TypeReference> classes;
-        caches.emplace_back(dex_pc, /*is_missing_types*/true, classes);
-      }
-      ProfileMethodInfo pmi(MethodReference(method->GetDexFile(),
-                                            method->GetDexMethodIndex()),
-                            caches);
-      profile_methods.push_back(pmi);
-      profile_methods_map->Put(method, pmi);
-    }
-
-    if (!info.AddMethods(profile_methods, flags)
-        || info.GetNumberOfMethods() != profile_methods.size()) {
-      return false;
-    }
-    return info.Save(filename, nullptr);
-  }
-
   // Creates an inline cache which will be destructed at the end of the test.
   ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() {
     used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap(
@@ -187,35 +81,6 @@
     return used_inline_caches.back().get();
   }
 
-  ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo(
-        const ProfileMethodInfo& pmi) {
-    ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
-    ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map);
-    SafeMap<DexFile*, uint8_t> dex_map;  // dex files to profile index
-    for (const auto& inline_cache : pmi.inline_caches) {
-      ProfileCompilationInfo::DexPcData& dex_pc_data =
-          ic_map->FindOrAdd(
-              inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(allocator_.get()))->second;
-      if (inline_cache.is_missing_types) {
-        dex_pc_data.SetIsMissingTypes();
-      }
-      for (const auto& class_ref : inline_cache.classes) {
-        uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file),
-                                                      static_cast<uint8_t>(dex_map.size()))->second;
-        dex_pc_data.AddClass(dex_profile_index, class_ref.TypeIndex());
-        if (dex_profile_index >= offline_pmi.dex_references.size()) {
-          // This is a new dex.
-          const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey(
-              class_ref.dex_file->GetLocation());
-          offline_pmi.dex_references.emplace_back(dex_key,
-                                                  class_ref.dex_file->GetLocationChecksum(),
-                                                  class_ref.dex_file->NumMethodIds());
-        }
-      }
-    }
-    return offline_pmi;
-  }
-
   // Creates an offline profile used for testing inline caches.
   ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() {
     ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
@@ -261,7 +126,7 @@
     ProfileCompilationInfo::InlineCacheMap* ic_map =
         const_cast<ProfileCompilationInfo::InlineCacheMap*>(pmi->inline_caches);
     for (auto it : *ic_map) {
-      for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) {
+      for (uint16_t k = 0; k <= 2 * ProfileCompilationInfo::kIndividualInlineCacheSize; k++) {
         it.second.AddClass(0, dex::TypeIndex(k));
       }
     }
@@ -327,6 +192,7 @@
   static constexpr int kProfileMagicSize = 4;
   static constexpr int kProfileVersionSize = 4;
 
+  MallocArenaPool pool_;
   std::unique_ptr<ArenaAllocator> allocator_;
 
   // Cache of inline caches generated during tests.
@@ -335,61 +201,6 @@
   std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
 };
 
-TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
-  ScratchFile profile;
-
-  Thread* self = Thread::Current();
-  jobject class_loader;
-  {
-    ScopedObjectAccess soa(self);
-    class_loader = LoadDex("ProfileTestMultiDex");
-  }
-  ASSERT_NE(class_loader, nullptr);
-
-  // Save virtual methods from Main.
-  std::set<DexCacheResolvedClasses> resolved_classes;
-  std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
-  ASSERT_TRUE(SaveProfilingInfo(
-      profile.GetFilename(), main_methods, resolved_classes, Hotness::kFlagPostStartup));
-
-  // Check that what we saved is in the profile.
-  ProfileCompilationInfo info1;
-  ASSERT_TRUE(info1.Load(GetFd(profile)));
-  ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size());
-  {
-    ScopedObjectAccess soa(self);
-    for (ArtMethod* m : main_methods) {
-      Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
-      ASSERT_TRUE(h.IsHot());
-      ASSERT_TRUE(h.IsPostStartup());
-    }
-  }
-
-  // Save virtual methods from Second.
-  std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
-  ASSERT_TRUE(SaveProfilingInfo(
-    profile.GetFilename(), second_methods, resolved_classes, Hotness::kFlagStartup));
-
-  // Check that what we saved is in the profile (methods form Main and Second).
-  ProfileCompilationInfo info2;
-  ASSERT_TRUE(profile.GetFile()->ResetOffset());
-  ASSERT_TRUE(info2.Load(GetFd(profile)));
-  ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size());
-  {
-    ScopedObjectAccess soa(self);
-    for (ArtMethod* m : main_methods) {
-      Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
-      ASSERT_TRUE(h.IsHot());
-      ASSERT_TRUE(h.IsPostStartup());
-    }
-    for (ArtMethod* m : second_methods) {
-      Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
-      ASSERT_TRUE(h.IsHot());
-      ASSERT_TRUE(h.IsStartup());
-    }
-  }
-}
-
 TEST_F(ProfileCompilationInfoTest, SaveFd) {
   ScratchFile profile;
 
@@ -722,48 +533,6 @@
   ASSERT_TRUE(*loaded_pmi1 == pmi_extra);
 }
 
-TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
-  ScratchFile profile;
-
-  Thread* self = Thread::Current();
-  jobject class_loader;
-  {
-    ScopedObjectAccess soa(self);
-    class_loader = LoadDex("ProfileTestMultiDex");
-  }
-  ASSERT_NE(class_loader, nullptr);
-
-  // Save virtual methods from Main.
-  std::set<DexCacheResolvedClasses> resolved_classes;
-  std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
-
-  SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
-  ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
-      profile.GetFilename(), main_methods, Hotness::kFlagStartup, &profile_methods_map));
-
-  // Check that what we saved is in the profile.
-  ProfileCompilationInfo info;
-  ASSERT_TRUE(info.Load(GetFd(profile)));
-  ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size());
-  {
-    ScopedObjectAccess soa(self);
-    for (ArtMethod* m : main_methods) {
-      Hotness h = info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
-      ASSERT_TRUE(h.IsHot());
-      ASSERT_TRUE(h.IsStartup());
-      const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
-      std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi =
-          info.GetMethod(m->GetDexFile()->GetLocation(),
-                         m->GetDexFile()->GetLocationChecksum(),
-                         m->GetDexMethodIndex());
-      ASSERT_TRUE(offline_pmi != nullptr);
-      ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi =
-          ConvertProfileMethodInfo(pmi);
-      ASSERT_EQ(converted_pmi, *offline_pmi);
-    }
-  }
-}
-
 TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCache) {
   ScratchFile profile;
 
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 1697092..9bea18a 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -715,7 +715,7 @@
     if (!klass->IsProxyClass() && klass->GetDexCache() != nullptr) {
       art::StackHandleScope<1> hs(soa.Self());
       art::Handle<art::mirror::Class> h_klass = hs.NewHandle(klass);
-      art::mirror::ObjectArray<art::mirror::String>* str_array =
+      art::ObjPtr<art::mirror::ObjectArray<art::mirror::String>> str_array =
           art::annotations::GetSignatureAnnotationForClass(h_klass);
       if (str_array != nullptr) {
         std::ostringstream oss;
diff --git a/openjdkjvmti/ti_field.cc b/openjdkjvmti/ti_field.cc
index 328e2a1..2a860d9 100644
--- a/openjdkjvmti/ti_field.cc
+++ b/openjdkjvmti/ti_field.cc
@@ -91,7 +91,7 @@
   if (generic_ptr != nullptr) {
     *generic_ptr = nullptr;
     if (!art_field->GetDeclaringClass()->IsProxyClass()) {
-      art::mirror::ObjectArray<art::mirror::String>* str_array =
+      art::ObjPtr<art::mirror::ObjectArray<art::mirror::String>> str_array =
           art::annotations::GetSignatureAnnotationForField(art_field);
       if (str_array != nullptr) {
         std::ostringstream oss;
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index c0c312c..d0b7224f 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -345,7 +345,7 @@
   if (generic_ptr != nullptr) {
     *generic_ptr = nullptr;
     if (!art_method->GetDeclaringClass()->IsProxyClass()) {
-      art::mirror::ObjectArray<art::mirror::String>* str_array =
+      art::ObjPtr<art::mirror::ObjectArray<art::mirror::String>> str_array =
           art::annotations::GetSignatureAnnotationForMethod(art_method);
       if (str_array != nullptr) {
         std::ostringstream oss;
diff --git a/runtime/Android.bp b/runtime/Android.bp
index b276c81..777a1fc 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -574,6 +574,7 @@
         "interpreter/safe_math_test.cc",
         "interpreter/unstarted_runtime_test.cc",
         "jdwp/jdwp_options_test.cc",
+        "jit/profiling_info_test.cc",
         "jni/java_vm_ext_test.cc",
         "method_handles_test.cc",
         "mirror/dex_cache_test.cc",
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 78516e3..b0c0e43 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -21,6 +21,7 @@
 #include "base/callee_save_type.h"
 #include "base/enums.h"
 #include "class_linker-inl.h"
+#include "class_root.h"
 #include "common_runtime_test.h"
 #include "entrypoints/quick/quick_entrypoints_enum.h"
 #include "imt_conflict_table.h"
@@ -2096,7 +2097,7 @@
 
   EXPECT_FALSE(self->IsExceptionPending());
 
-  GcRoot<mirror::Class>& root = mirror::String::java_lang_String_;
+  GcRoot<mirror::Class> root(GetClassRoot<mirror::String>());
   size_t result = Invoke3(reinterpret_cast<size_t>(&root), 0U, 0U, readBarrierForRootSlow, self);
 
   EXPECT_FALSE(self->IsExceptionPending());
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b882f65..0952723 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -484,27 +484,10 @@
                  mirror::ObjectArray<mirror::Object>::ClassSize(image_pointer_size_))));
   object_array_class->SetComponentType(java_lang_Object.Get());
 
-  // Setup the char (primitive) class to be used for char[].
-  Handle<mirror::Class> char_class(hs.NewHandle(
-      AllocClass(self, java_lang_Class.Get(),
-                 mirror::Class::PrimitiveClassSize(image_pointer_size_))));
-  // The primitive char class won't be initialized by
-  // InitializePrimitiveClass until line 459, but strings (and
-  // internal char arrays) will be allocated before that and the
-  // component size, which is computed from the primitive type, needs
-  // to be set here.
-  char_class->SetPrimitiveType(Primitive::kPrimChar);
-
-  // Setup the char[] class to be used for String.
-  Handle<mirror::Class> char_array_class(hs.NewHandle(
-      AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_))));
-  char_array_class->SetComponentType(char_class.Get());
-
   // Setup String.
   Handle<mirror::Class> java_lang_String(hs.NewHandle(
       AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize(image_pointer_size_))));
   java_lang_String->SetStringClass();
-  mirror::String::SetClass(java_lang_String.Get());
   mirror::Class::SetStatus(java_lang_String, ClassStatus::kResolved, self);
 
   // Setup java.lang.ref.Reference.
@@ -523,7 +506,6 @@
   SetClassRoot(ClassRoot::kJavaLangObject, java_lang_Object.Get());
   SetClassRoot(ClassRoot::kClassArrayClass, class_array_class.Get());
   SetClassRoot(ClassRoot::kObjectArrayClass, object_array_class.Get());
-  SetClassRoot(ClassRoot::kCharArrayClass, char_array_class.Get());
   SetClassRoot(ClassRoot::kJavaLangString, java_lang_String.Get());
   SetClassRoot(ClassRoot::kJavaLangRefReference, java_lang_ref_Reference.Get());
 
@@ -533,6 +515,7 @@
   // Setup the primitive type classes.
   SetClassRoot(ClassRoot::kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean));
   SetClassRoot(ClassRoot::kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte));
+  SetClassRoot(ClassRoot::kPrimitiveChar, CreatePrimitiveClass(self, Primitive::kPrimChar));
   SetClassRoot(ClassRoot::kPrimitiveShort, CreatePrimitiveClass(self, Primitive::kPrimShort));
   SetClassRoot(ClassRoot::kPrimitiveInt, CreatePrimitiveClass(self, Primitive::kPrimInt));
   SetClassRoot(ClassRoot::kPrimitiveLong, CreatePrimitiveClass(self, Primitive::kPrimLong));
@@ -543,13 +526,13 @@
   // Create array interface entries to populate once we can load system classes.
   array_iftable_ = GcRoot<mirror::IfTable>(AllocIfTable(self, 2));
 
-  // Create int array type for AllocDexCache (done in AppendToBootClassPath).
+  // Create int array type for native pointer arrays (for example vtables) on 32-bit archs.
   Handle<mirror::Class> int_array_class(hs.NewHandle(
       AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_))));
   int_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveInt, this));
   SetClassRoot(ClassRoot::kIntArrayClass, int_array_class.Get());
 
-  // Create long array type for AllocDexCache (done in AppendToBootClassPath).
+  // Create long array type for native pointer arrays (for example vtables) on 64-bit archs.
   Handle<mirror::Class> long_array_class(hs.NewHandle(
       AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_))));
   long_array_class->SetComponentType(GetClassRoot(ClassRoot::kPrimitiveLong, this));
@@ -604,10 +587,6 @@
 
   // now we can use FindSystemClass
 
-  // run char class through InitializePrimitiveClass to finish init
-  InitializePrimitiveClass(char_class.Get(), Primitive::kPrimChar);
-  SetClassRoot(ClassRoot::kPrimitiveChar, char_class.Get());  // needs descriptor
-
   // Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that
   // we do not need friend classes or a publicly exposed setter.
   quick_generic_jni_trampoline_ = GetQuickGenericJniStub();
@@ -636,7 +615,7 @@
 
   SetClassRoot(ClassRoot::kByteArrayClass, FindSystemClass(self, "[B"));
 
-  CheckSystemClass(self, char_array_class, "[C");
+  SetClassRoot(ClassRoot::kCharArrayClass, FindSystemClass(self, "[C"));
 
   SetClassRoot(ClassRoot::kShortArrayClass, FindSystemClass(self, "[S"));
 
@@ -685,7 +664,7 @@
                FindSystemClass(self, "Ljava/lang/reflect/Proxy;"));
 
   // Create java.lang.reflect.Field.class root.
-  auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;");
+  ObjPtr<mirror::Class> class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;");
   CHECK(class_root != nullptr);
   SetClassRoot(ClassRoot::kJavaLangReflectField, class_root);
 
@@ -1014,10 +993,6 @@
           spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)));
   mirror::Class::SetClassClass(GetClassRoot(ClassRoot::kJavaLangClass, this));
 
-  // Special case of setting up the String class early so that we can test arbitrary objects
-  // as being Strings or not
-  mirror::String::SetClass(GetClassRoot<mirror::String>(this));
-
   ObjPtr<mirror::Class> java_lang_Object = GetClassRoot<mirror::Object>(this);
   java_lang_Object->SetObjectSize(sizeof(mirror::Object));
   // Allocate in non-movable so that it's possible to check if a JNI weak global ref has been
@@ -2109,7 +2084,6 @@
 
 ClassLinker::~ClassLinker() {
   mirror::Class::ResetClass();
-  mirror::String::ResetClass();
   Thread* const self = Thread::Current();
   for (const ClassLoaderData& data : class_loaders_) {
     // CHA unloading analysis is not needed. No negative consequences are expected because
@@ -3561,20 +3535,13 @@
 }
 
 mirror::Class* ClassLinker::CreatePrimitiveClass(Thread* self, Primitive::Type type) {
-  ObjPtr<mirror::Class> klass =
+  ObjPtr<mirror::Class> primitive_class =
       AllocClass(self, mirror::Class::PrimitiveClassSize(image_pointer_size_));
-  if (UNLIKELY(klass == nullptr)) {
+  if (UNLIKELY(primitive_class == nullptr)) {
     self->AssertPendingOOMException();
     return nullptr;
   }
-  return InitializePrimitiveClass(klass, type);
-}
-
-mirror::Class* ClassLinker::InitializePrimitiveClass(ObjPtr<mirror::Class> primitive_class,
-                                                     Primitive::Type type) {
-  CHECK(primitive_class != nullptr);
   // Must hold lock on object when initializing.
-  Thread* self = Thread::Current();
   StackHandleScope<1> hs(self);
   Handle<mirror::Class> h_class(hs.NewHandle(primitive_class));
   ObjectLock<mirror::Class> lock(self, h_class);
@@ -3668,8 +3635,6 @@
       new_class.Assign(GetClassRoot<mirror::ObjectArray<mirror::Object>>(this));
     } else if (strcmp(descriptor, "[Ljava/lang/String;") == 0) {
       new_class.Assign(GetClassRoot<mirror::ObjectArray<mirror::String>>(this));
-    } else if (strcmp(descriptor, "[C") == 0) {
-      new_class.Assign(GetClassRoot<mirror::CharArray>(this));
     } else if (strcmp(descriptor, "[I") == 0) {
       new_class.Assign(GetClassRoot<mirror::IntArray>(this));
     } else if (strcmp(descriptor, "[J") == 0) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index afe5c99..1f94c43 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -793,10 +793,6 @@
   mirror::Class* CreatePrimitiveClass(Thread* self, Primitive::Type type)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Roles::uninterruptible_);
-  mirror::Class* InitializePrimitiveClass(ObjPtr<mirror::Class> primitive_class,
-                                          Primitive::Type type)
-      REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(!Roles::uninterruptible_);
 
   mirror::Class* CreateArrayClass(Thread* self,
                                   const char* descriptor,
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index ffa0a90..5cb08dc 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -849,7 +849,8 @@
   return annotation_value.value_.GetL();
 }
 
-mirror::ObjectArray<mirror::String>* GetSignatureValue(const ClassData& klass,
+static ObjPtr<mirror::ObjectArray<mirror::String>> GetSignatureValue(
+    const ClassData& klass,
     const DexFile::AnnotationSetItem* annotation_set)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   const DexFile& dex_file = klass.GetDexFile();
@@ -860,12 +861,9 @@
   if (annotation_item == nullptr) {
     return nullptr;
   }
-  ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
-  Handle<mirror::Class> string_array_class(hs.NewHandle(
-      Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class)));
-  if (string_array_class == nullptr) {
-    return nullptr;
-  }
+  Handle<mirror::Class> string_array_class =
+      hs.NewHandle(GetClassRoot<mirror::ObjectArray<mirror::String>>());
+  DCHECK(string_array_class != nullptr);
   ObjPtr<mirror::Object> obj =
       GetAnnotationValue(klass, annotation_item, "value", string_array_class,
                          DexFile::kDexAnnotationArray);
@@ -880,19 +878,16 @@
     const DexFile::AnnotationSetItem* annotation_set)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   const DexFile& dex_file = klass.GetDexFile();
-  StackHandleScope<1> hs(Thread::Current());
   const DexFile::AnnotationItem* annotation_item =
       SearchAnnotationSet(dex_file, annotation_set, "Ldalvik/annotation/Throws;",
                           DexFile::kDexVisibilitySystem);
   if (annotation_item == nullptr) {
     return nullptr;
   }
-  ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
-  Handle<mirror::Class> class_array_class(hs.NewHandle(
-      Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &class_class)));
-  if (class_array_class == nullptr) {
-    return nullptr;
-  }
+  StackHandleScope<1> hs(Thread::Current());
+  Handle<mirror::Class> class_array_class =
+      hs.NewHandle(GetClassRoot<mirror::ObjectArray<mirror::Class>>());
+  DCHECK(class_array_class != nullptr);
   ObjPtr<mirror::Object> obj =
       GetAnnotationValue(klass, annotation_item, "value", class_array_class,
                          DexFile::kDexAnnotationArray);
@@ -1020,7 +1015,7 @@
   return ProcessAnnotationSet(field_class, annotation_set, DexFile::kDexVisibilityRuntime);
 }
 
-mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForField(ArtField* field) {
+ObjPtr<mirror::ObjectArray<mirror::String>> GetSignatureAnnotationForField(ArtField* field) {
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForField(field);
   if (annotation_set == nullptr) {
     return nullptr;
@@ -1171,9 +1166,10 @@
                                               annotation_class);
 }
 
-bool GetParametersMetadataForMethod(ArtMethod* method,
-                                    MutableHandle<mirror::ObjectArray<mirror::String>>* names,
-                                    MutableHandle<mirror::IntArray>* access_flags) {
+bool GetParametersMetadataForMethod(
+    ArtMethod* method,
+    /*out*/ MutableHandle<mirror::ObjectArray<mirror::String>>* names,
+    /*out*/ MutableHandle<mirror::IntArray>* access_flags) {
   const DexFile::AnnotationSetItem* annotation_set =
       FindAnnotationSetForMethod(method);
   if (annotation_set == nullptr) {
@@ -1193,12 +1189,10 @@
   StackHandleScope<4> hs(Thread::Current());
 
   // Extract the parameters' names String[].
-  ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
-  Handle<mirror::Class> string_array_class(hs.NewHandle(
-      Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class)));
-  if (UNLIKELY(string_array_class == nullptr)) {
-    return false;
-  }
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Handle<mirror::Class> string_array_class =
+      hs.NewHandle(GetClassRoot<mirror::ObjectArray<mirror::String>>(class_linker));
+  DCHECK(string_array_class != nullptr);
 
   ClassData data(method);
   Handle<mirror::Object> names_obj =
@@ -1212,10 +1206,8 @@
   }
 
   // Extract the parameters' access flags int[].
-  Handle<mirror::Class> int_array_class(hs.NewHandle(GetClassRoot<mirror::IntArray>()));
-  if (UNLIKELY(int_array_class == nullptr)) {
-    return false;
-  }
+  Handle<mirror::Class> int_array_class(hs.NewHandle(GetClassRoot<mirror::IntArray>(class_linker)));
+  DCHECK(int_array_class != nullptr);
   Handle<mirror::Object> access_flags_obj =
       hs.NewHandle(GetAnnotationValue(data,
                                       annotation_item,
@@ -1226,12 +1218,12 @@
     return false;
   }
 
-  names->Assign(names_obj.Get()->AsObjectArray<mirror::String>());
-  access_flags->Assign(access_flags_obj.Get()->AsIntArray());
+  names->Assign(names_obj->AsObjectArray<mirror::String>());
+  access_flags->Assign(access_flags_obj->AsIntArray());
   return true;
 }
 
-mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForMethod(ArtMethod* method) {
+ObjPtr<mirror::ObjectArray<mirror::String>> GetSignatureAnnotationForMethod(ArtMethod* method) {
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method);
   if (annotation_set == nullptr) {
     return nullptr;
@@ -1345,12 +1337,9 @@
     return nullptr;
   }
   StackHandleScope<1> hs(Thread::Current());
-  ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
-  Handle<mirror::Class> class_array_class(hs.NewHandle(
-      Runtime::Current()->GetClassLinker()->FindArrayClass(hs.Self(), &class_class)));
-  if (class_array_class == nullptr) {
-    return nullptr;
-  }
+  Handle<mirror::Class> class_array_class =
+      hs.NewHandle(GetClassRoot<mirror::ObjectArray<mirror::Class>>());
+  DCHECK(class_array_class != nullptr);
   ObjPtr<mirror::Object> obj =
       GetAnnotationValue(data, annotation_item, "value", class_array_class,
                          DexFile::kDexAnnotationArray);
@@ -1446,7 +1435,7 @@
       DexFile::kDexAnnotationMethod);
 }
 
-bool GetInnerClass(Handle<mirror::Class> klass, ObjPtr<mirror::String>* name) {
+bool GetInnerClass(Handle<mirror::Class> klass, /*out*/ ObjPtr<mirror::String>* name) {
   ClassData data(klass);
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
   if (annotation_set == nullptr) {
@@ -1513,7 +1502,8 @@
   return true;
 }
 
-mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForClass(Handle<mirror::Class> klass) {
+ObjPtr<mirror::ObjectArray<mirror::String>> GetSignatureAnnotationForClass(
+    Handle<mirror::Class> klass) {
   ClassData data(klass);
   const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
   if (annotation_set == nullptr) {
diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h
index 9645a7f..bde7891 100644
--- a/runtime/dex/dex_file_annotations.h
+++ b/runtime/dex/dex_file_annotations.h
@@ -41,7 +41,7 @@
     REQUIRES_SHARED(Locks::mutator_lock_);
 ObjPtr<mirror::ObjectArray<mirror::Object>> GetAnnotationsForField(ArtField* field)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForField(ArtField* field)
+ObjPtr<mirror::ObjectArray<mirror::String>> GetSignatureAnnotationForField(ArtField* field)
     REQUIRES_SHARED(Locks::mutator_lock_);
 bool IsFieldAnnotationPresent(ArtField* field, Handle<mirror::Class> annotation_class)
     REQUIRES_SHARED(Locks::mutator_lock_);
@@ -64,11 +64,11 @@
                                                        uint32_t parameter_idx,
                                                        Handle<mirror::Class> annotation_class)
     REQUIRES_SHARED(Locks::mutator_lock_);
-bool GetParametersMetadataForMethod(ArtMethod* method,
-                                    MutableHandle<mirror::ObjectArray<mirror::String>>* names,
-                                    MutableHandle<mirror::IntArray>* access_flags)
-    REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForMethod(ArtMethod* method)
+bool GetParametersMetadataForMethod(
+    ArtMethod* method,
+    /*out*/ MutableHandle<mirror::ObjectArray<mirror::String>>* names,
+    /*out*/ MutableHandle<mirror::IntArray>* access_flags) REQUIRES_SHARED(Locks::mutator_lock_);
+ObjPtr<mirror::ObjectArray<mirror::String>> GetSignatureAnnotationForMethod(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_);
 // Check whether `method` is annotated with `annotation_class`.
 // If `lookup_in_resolved_boot_classes` is true, look up any of the
@@ -101,12 +101,12 @@
     REQUIRES_SHARED(Locks::mutator_lock_);
 ObjPtr<mirror::Object> GetEnclosingMethod(Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_);
-bool GetInnerClass(Handle<mirror::Class> klass, ObjPtr<mirror::String>* name)
+bool GetInnerClass(Handle<mirror::Class> klass, /*out*/ ObjPtr<mirror::String>* name)
     REQUIRES_SHARED(Locks::mutator_lock_);
 bool GetInnerClassFlags(Handle<mirror::Class> klass, uint32_t* flags)
     REQUIRES_SHARED(Locks::mutator_lock_);
-mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForClass(Handle<mirror::Class> klass)
-    REQUIRES_SHARED(Locks::mutator_lock_);
+ObjPtr<mirror::ObjectArray<mirror::String>> GetSignatureAnnotationForClass(
+    Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
 const char* GetSourceDebugExtension(Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_);
 bool IsClassAnnotationPresent(Handle<mirror::Class> klass,
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 37234e1..0ee780d 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -35,6 +35,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "class_linker-inl.h"
+#include "class_root.h"
 #include "common_dex_operations.h"
 #include "common_throws.h"
 #include "dex/dex_file-inl.h"
@@ -328,7 +329,7 @@
                                                    ShadowFrame& shadow_frame,
                                                    dex::StringIndex string_idx)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  ObjPtr<mirror::Class> java_lang_string_class = mirror::String::GetJavaLangString();
+  ObjPtr<mirror::Class> java_lang_string_class = GetClassRoot<mirror::String>();
   if (UNLIKELY(!java_lang_string_class->IsInitialized())) {
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     StackHandleScope<1> hs(self);
diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S
index 7017dd1..0722804 100644
--- a/runtime/interpreter/mterp/arm64/header.S
+++ b/runtime/interpreter/mterp/arm64/header.S
@@ -339,6 +339,7 @@
  */
 .macro ENTRY name
     .type \name, #function
+    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
     .global \name
     /* Cache alignment for function entry */
     .balign 16
diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S
index d5374d2..70f71ff 100644
--- a/runtime/interpreter/mterp/out/mterp_arm64.S
+++ b/runtime/interpreter/mterp/out/mterp_arm64.S
@@ -346,6 +346,7 @@
  */
 .macro ENTRY name
     .type \name, #function
+    .hidden \name  // Hide this as a global symbol, so we do not incur plt calls.
     .global \name
     /* Cache alignment for function entry */
     .balign 16
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index 6f4752f..1eacfe8 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -106,11 +106,13 @@
     #define SIZE(start,end)
     // Mac OS' symbols have an _ prefix.
     #define SYMBOL(name) _ ## name
+    #define ASM_HIDDEN .private_extern
 #else
     #define MACRO_LITERAL(value) $value
     #define FUNCTION_TYPE(name) .type name, @function
     #define SIZE(start,end) .size start, .-end
     #define SYMBOL(name) name
+    #define ASM_HIDDEN .hidden
 #endif
 
 .macro PUSH _reg
@@ -339,6 +341,7 @@
  */
 
     .text
+    ASM_HIDDEN SYMBOL(ExecuteMterpImpl)
     .global SYMBOL(ExecuteMterpImpl)
     FUNCTION_TYPE(ExecuteMterpImpl)
 
diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S
index fca2515..ea8f483 100644
--- a/runtime/interpreter/mterp/out/mterp_x86_64.S
+++ b/runtime/interpreter/mterp/out/mterp_x86_64.S
@@ -102,11 +102,13 @@
     #define SIZE(start,end)
     // Mac OS' symbols have an _ prefix.
     #define SYMBOL(name) _ ## name
+    #define ASM_HIDDEN .private_extern
 #else
     #define MACRO_LITERAL(value) $value
     #define FUNCTION_TYPE(name) .type name, @function
     #define SIZE(start,end) .size start, .-end
     #define SYMBOL(name) name
+    #define ASM_HIDDEN .hidden
 #endif
 
 .macro PUSH _reg
@@ -325,6 +327,7 @@
  */
 
     .text
+    ASM_HIDDEN SYMBOL(ExecuteMterpImpl)
     .global SYMBOL(ExecuteMterpImpl)
     FUNCTION_TYPE(ExecuteMterpImpl)
 
diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S
index 324637b..939dc61 100644
--- a/runtime/interpreter/mterp/x86/entry.S
+++ b/runtime/interpreter/mterp/x86/entry.S
@@ -18,6 +18,7 @@
  */
 
     .text
+    ASM_HIDDEN SYMBOL(ExecuteMterpImpl)
     .global SYMBOL(ExecuteMterpImpl)
     FUNCTION_TYPE(ExecuteMterpImpl)
 
diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S
index 9d826c2..6f31228 100644
--- a/runtime/interpreter/mterp/x86/header.S
+++ b/runtime/interpreter/mterp/x86/header.S
@@ -99,11 +99,13 @@
     #define SIZE(start,end)
     // Mac OS' symbols have an _ prefix.
     #define SYMBOL(name) _ ## name
+    #define ASM_HIDDEN .private_extern
 #else
     #define MACRO_LITERAL(value) $$value
     #define FUNCTION_TYPE(name) .type name, @function
     #define SIZE(start,end) .size start, .-end
     #define SYMBOL(name) name
+    #define ASM_HIDDEN .hidden
 #endif
 
 .macro PUSH _reg
diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S
index 2f69226..b08419b 100644
--- a/runtime/interpreter/mterp/x86_64/entry.S
+++ b/runtime/interpreter/mterp/x86_64/entry.S
@@ -18,6 +18,7 @@
  */
 
     .text
+    ASM_HIDDEN SYMBOL(ExecuteMterpImpl)
     .global SYMBOL(ExecuteMterpImpl)
     FUNCTION_TYPE(ExecuteMterpImpl)
 
diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S
index 5563810..4ebe95e 100644
--- a/runtime/interpreter/mterp/x86_64/header.S
+++ b/runtime/interpreter/mterp/x86_64/header.S
@@ -95,11 +95,13 @@
     #define SIZE(start,end)
     // Mac OS' symbols have an _ prefix.
     #define SYMBOL(name) _ ## name
+    #define ASM_HIDDEN .private_extern
 #else
     #define MACRO_LITERAL(value) $$value
     #define FUNCTION_TYPE(name) .type name, @function
     #define SIZE(start,end) .size start, .-end
     #define SYMBOL(name) name
+    #define ASM_HIDDEN .hidden
 #endif
 
 .macro PUSH _reg
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 9bb760c..88cfafb 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -388,7 +388,7 @@
 TEST_F(UnstartedRuntimeTest, StringInit) {
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
-  ObjPtr<mirror::Class> klass = mirror::String::GetJavaLangString();
+  ObjPtr<mirror::Class> klass = GetClassRoot<mirror::String>();
   ArtMethod* method =
       klass->FindConstructor("(Ljava/lang/String;)V",
                              Runtime::Current()->GetClassLinker()->GetImagePointerSize());
@@ -537,7 +537,7 @@
                  tmp,
                  false,
                  object_class.Get(),
-                 mirror::String::GetJavaLangString(),
+                 GetClassRoot<mirror::String>(),
                  hs_src,
                  1,
                  hs_dst,
@@ -551,7 +551,7 @@
   {
     StackHandleScope<3> hs_src(self);
     hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "1"));
-    hs_src.NewHandle(mirror::String::GetJavaLangString());
+    hs_src.NewHandle(GetClassRoot<mirror::String>());
     hs_src.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "3"));
 
     StackHandleScope<3> hs_dst(self);
@@ -568,7 +568,7 @@
                  tmp,
                  true,
                  object_class.Get(),
-                 mirror::String::GetJavaLangString(),
+                 GetClassRoot<mirror::String>(),
                  hs_src,
                  0,
                  hs_dst,
diff --git a/runtime/jit/profiling_info_test.cc b/runtime/jit/profiling_info_test.cc
new file mode 100644
index 0000000..106a80a
--- /dev/null
+++ b/runtime/jit/profiling_info_test.cc
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <stdio.h>
+
+#include "art_method-inl.h"
+#include "base/unix_file/fd_file.h"
+#include "class_linker-inl.h"
+#include "common_runtime_test.h"
+#include "dex/dex_file.h"
+#include "dex/dex_file_loader.h"
+#include "dex/method_reference.h"
+#include "dex/type_reference.h"
+#include "handle_scope-inl.h"
+#include "linear_alloc.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "profile/profile_compilation_info.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ziparchive/zip_writer.h"
+
+namespace art {
+
+using Hotness = ProfileCompilationInfo::MethodHotness;
+
+static constexpr size_t kMaxMethodIds = 65535;
+
+class ProfileCompilationInfoTest : public CommonRuntimeTest {
+ public:
+  void PostRuntimeCreate() OVERRIDE {
+    allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
+  }
+
+ protected:
+  std::vector<ArtMethod*> GetVirtualMethods(jobject class_loader,
+                                            const std::string& clazz) {
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    Thread* self = Thread::Current();
+    ScopedObjectAccess soa(self);
+    StackHandleScope<1> hs(self);
+    Handle<mirror::ClassLoader> h_loader(
+        hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader()));
+    ObjPtr<mirror::Class> klass = class_linker->FindClass(self, clazz.c_str(), h_loader);
+
+    const auto pointer_size = class_linker->GetImagePointerSize();
+    std::vector<ArtMethod*> methods;
+    for (auto& m : klass->GetVirtualMethods(pointer_size)) {
+      methods.push_back(&m);
+    }
+    return methods;
+  }
+
+  bool AddMethod(const std::string& dex_location,
+                 uint32_t checksum,
+                 uint16_t method_index,
+                 ProfileCompilationInfo* info) {
+    return info->AddMethodIndex(Hotness::kFlagHot,
+                                dex_location,
+                                checksum,
+                                method_index,
+                                kMaxMethodIds);
+  }
+
+  bool AddMethod(const std::string& dex_location,
+                 uint32_t checksum,
+                 uint16_t method_index,
+                 const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi,
+                 ProfileCompilationInfo* info) {
+    return info->AddMethod(
+        dex_location, checksum, method_index, kMaxMethodIds, pmi, Hotness::kFlagPostStartup);
+  }
+
+  bool AddClass(const std::string& dex_location,
+                uint32_t checksum,
+                dex::TypeIndex type_index,
+                ProfileCompilationInfo* info) {
+    DexCacheResolvedClasses classes(dex_location, dex_location, checksum, kMaxMethodIds);
+    classes.AddClass(type_index);
+    return info->AddClasses({classes});
+  }
+
+  uint32_t GetFd(const ScratchFile& file) {
+    return static_cast<uint32_t>(file.GetFd());
+  }
+
+  bool SaveProfilingInfo(
+      const std::string& filename,
+      const std::vector<ArtMethod*>& methods,
+      const std::set<DexCacheResolvedClasses>& resolved_classes,
+      Hotness::Flag flags) {
+    ProfileCompilationInfo info;
+    std::vector<ProfileMethodInfo> profile_methods;
+    ScopedObjectAccess soa(Thread::Current());
+    for (ArtMethod* method : methods) {
+      profile_methods.emplace_back(
+          MethodReference(method->GetDexFile(), method->GetDexMethodIndex()));
+    }
+    if (!info.AddMethods(profile_methods, flags) || !info.AddClasses(resolved_classes)) {
+      return false;
+    }
+    if (info.GetNumberOfMethods() != profile_methods.size()) {
+      return false;
+    }
+    ProfileCompilationInfo file_profile;
+    if (!file_profile.Load(filename, false)) {
+      return false;
+    }
+    if (!info.MergeWith(file_profile)) {
+      return false;
+    }
+
+    return info.Save(filename, nullptr);
+  }
+
+  // Saves the given art methods to a profile backed by 'filename' and adds
+  // some fake inline caches to it. The added inline caches are returned in
+  // the out map `profile_methods_map`.
+  bool SaveProfilingInfoWithFakeInlineCaches(
+      const std::string& filename,
+      const std::vector<ArtMethod*>& methods,
+      Hotness::Flag flags,
+      /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) {
+    ProfileCompilationInfo info;
+    std::vector<ProfileMethodInfo> profile_methods;
+    ScopedObjectAccess soa(Thread::Current());
+    for (ArtMethod* method : methods) {
+      std::vector<ProfileMethodInfo::ProfileInlineCache> caches;
+      // Monomorphic
+      for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
+        std::vector<TypeReference> classes;
+        classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0));
+        caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
+      }
+      // Polymorphic
+      for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
+        std::vector<TypeReference> classes;
+        for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) {
+          classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
+        }
+        caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
+      }
+      // Megamorphic
+      for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
+        std::vector<TypeReference> classes;
+        for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) {
+          classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
+        }
+        caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
+      }
+      // Missing types
+      for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
+        std::vector<TypeReference> classes;
+        caches.emplace_back(dex_pc, /*is_missing_types*/true, classes);
+      }
+      ProfileMethodInfo pmi(MethodReference(method->GetDexFile(),
+                                            method->GetDexMethodIndex()),
+                            caches);
+      profile_methods.push_back(pmi);
+      profile_methods_map->Put(method, pmi);
+    }
+
+    if (!info.AddMethods(profile_methods, flags)
+        || info.GetNumberOfMethods() != profile_methods.size()) {
+      return false;
+    }
+    return info.Save(filename, nullptr);
+  }
+
+  // Creates an inline cache which will be destructed at the end of the test.
+  ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() {
+    used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap(
+        std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)));
+    return used_inline_caches.back().get();
+  }
+
+  ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo(
+        const ProfileMethodInfo& pmi) {
+    ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
+    ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map);
+    SafeMap<DexFile*, uint8_t> dex_map;  // dex files to profile index
+    for (const auto& inline_cache : pmi.inline_caches) {
+      ProfileCompilationInfo::DexPcData& dex_pc_data =
+          ic_map->FindOrAdd(
+              inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(allocator_.get()))->second;
+      if (inline_cache.is_missing_types) {
+        dex_pc_data.SetIsMissingTypes();
+      }
+      for (const auto& class_ref : inline_cache.classes) {
+        uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file),
+                                                      static_cast<uint8_t>(dex_map.size()))->second;
+        dex_pc_data.AddClass(dex_profile_index, class_ref.TypeIndex());
+        if (dex_profile_index >= offline_pmi.dex_references.size()) {
+          // This is a new dex.
+          const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey(
+              class_ref.dex_file->GetLocation());
+          offline_pmi.dex_references.emplace_back(dex_key,
+                                                  class_ref.dex_file->GetLocationChecksum(),
+                                                  class_ref.dex_file->NumMethodIds());
+        }
+      }
+    }
+    return offline_pmi;
+  }
+
+  // Cannot sizeof the actual arrays so hard code the values here.
+  // They should not change anyway.
+  static constexpr int kProfileMagicSize = 4;
+  static constexpr int kProfileVersionSize = 4;
+
+  std::unique_ptr<ArenaAllocator> allocator_;
+
+  // Cache of inline caches generated during tests.
+  // This makes it easier to pass data between different utilities and ensure that
+  // caches are destructed at the end of the test.
+  std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
+};
+
+TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
+  ScratchFile profile;
+
+  Thread* self = Thread::Current();
+  jobject class_loader;
+  {
+    ScopedObjectAccess soa(self);
+    class_loader = LoadDex("ProfileTestMultiDex");
+  }
+  ASSERT_NE(class_loader, nullptr);
+
+  // Save virtual methods from Main.
+  std::set<DexCacheResolvedClasses> resolved_classes;
+  std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
+  ASSERT_TRUE(SaveProfilingInfo(
+      profile.GetFilename(), main_methods, resolved_classes, Hotness::kFlagPostStartup));
+
+  // Check that what we saved is in the profile.
+  ProfileCompilationInfo info1;
+  ASSERT_TRUE(info1.Load(GetFd(profile)));
+  ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size());
+  {
+    ScopedObjectAccess soa(self);
+    for (ArtMethod* m : main_methods) {
+      Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
+      ASSERT_TRUE(h.IsHot());
+      ASSERT_TRUE(h.IsPostStartup());
+    }
+  }
+
+  // Save virtual methods from Second.
+  std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
+  ASSERT_TRUE(SaveProfilingInfo(
+    profile.GetFilename(), second_methods, resolved_classes, Hotness::kFlagStartup));
+
+  // Check that what we saved is in the profile (methods form Main and Second).
+  ProfileCompilationInfo info2;
+  ASSERT_TRUE(profile.GetFile()->ResetOffset());
+  ASSERT_TRUE(info2.Load(GetFd(profile)));
+  ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size());
+  {
+    ScopedObjectAccess soa(self);
+    for (ArtMethod* m : main_methods) {
+      Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
+      ASSERT_TRUE(h.IsHot());
+      ASSERT_TRUE(h.IsPostStartup());
+    }
+    for (ArtMethod* m : second_methods) {
+      Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
+      ASSERT_TRUE(h.IsHot());
+      ASSERT_TRUE(h.IsStartup());
+    }
+  }
+}
+
+TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
+  ScratchFile profile;
+
+  Thread* self = Thread::Current();
+  jobject class_loader;
+  {
+    ScopedObjectAccess soa(self);
+    class_loader = LoadDex("ProfileTestMultiDex");
+  }
+  ASSERT_NE(class_loader, nullptr);
+
+  // Save virtual methods from Main.
+  std::set<DexCacheResolvedClasses> resolved_classes;
+  std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
+
+  SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
+  ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
+      profile.GetFilename(), main_methods, Hotness::kFlagStartup, &profile_methods_map));
+
+  // Check that what we saved is in the profile.
+  ProfileCompilationInfo info;
+  ASSERT_TRUE(info.Load(GetFd(profile)));
+  ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size());
+  {
+    ScopedObjectAccess soa(self);
+    for (ArtMethod* m : main_methods) {
+      Hotness h = info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
+      ASSERT_TRUE(h.IsHot());
+      ASSERT_TRUE(h.IsStartup());
+      const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
+      std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi =
+          info.GetMethod(m->GetDexFile()->GetLocation(),
+                         m->GetDexFile()->GetLocationChecksum(),
+                         m->GetDexMethodIndex());
+      ASSERT_TRUE(offline_pmi != nullptr);
+      ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi =
+          ConvertProfileMethodInfo(pmi);
+      ASSERT_EQ(converted_pmi, *offline_pmi);
+    }
+  }
+}
+
+}  // namespace art
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index e6bfe55..cb2708d 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -1461,12 +1461,12 @@
   // circularity issue during loading the names of its members
   DCHECK(IsIdxLoaded<kVerifyFlags>() || IsRetired<kVerifyFlags>() ||
          IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>() ||
-         this == String::GetJavaLangString())
+         this == GetClassRoot<String>())
               << "IsIdxLoaded=" << IsIdxLoaded<kVerifyFlags>()
               << " IsRetired=" << IsRetired<kVerifyFlags>()
               << " IsErroneous=" <<
               IsErroneous<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>()
-              << " IsString=" << (this == String::GetJavaLangString())
+              << " IsString=" << (this == GetClassRoot<String>())
               << " status= " << GetStatus<kVerifyFlags>()
               << " descriptor=" << PrettyDescriptor();
 }
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index e6079c0..8fa2c6c 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -25,6 +25,7 @@
 #include "base/globals.h"
 #include "base/utils.h"
 #include "class.h"
+#include "class_root.h"
 #include "common_throws.h"
 #include "dex/utf.h"
 #include "gc/heap-inl.h"
@@ -211,7 +212,8 @@
   // http://b/23528461
   size_t alloc_size = RoundUp(size, kObjectAlignment);
 
-  Class* string_class = GetJavaLangString();
+  Runtime* runtime = Runtime::Current();
+  ObjPtr<Class> string_class = GetClassRoot<String>(runtime->GetClassLinker());
   // Check for overflow and throw OutOfMemoryError if this was an unreasonable request.
   // Do this by comparing with the maximum length that will _not_ cause an overflow.
   const size_t overflow_length = (-header_size) / block_size;   // Unsigned arithmetic.
@@ -227,7 +229,7 @@
     return nullptr;
   }
 
-  gc::Heap* heap = Runtime::Current()->GetHeap();
+  gc::Heap* heap = runtime->GetHeap();
   return down_cast<String*>(
       heap->AllocObjectWithAllocator<kIsInstrumented, true>(self, string_class, alloc_size,
                                                             allocator_type, pre_fence_visitor));
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 6208a96..b76ca19 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -35,9 +35,6 @@
 namespace art {
 namespace mirror {
 
-// TODO: get global references for these
-GcRoot<Class> String::java_lang_String_;
-
 int32_t String::FastIndexOf(int32_t ch, int32_t start) {
   int32_t count = GetLength();
   if (start < 0) {
@@ -52,18 +49,6 @@
   }
 }
 
-void String::SetClass(ObjPtr<Class> java_lang_String) {
-  CHECK(java_lang_String_.IsNull());
-  CHECK(java_lang_String != nullptr);
-  CHECK(java_lang_String->IsStringClass());
-  java_lang_String_ = GcRoot<Class>(java_lang_String);
-}
-
-void String::ResetClass() {
-  CHECK(!java_lang_String_.IsNull());
-  java_lang_String_ = GcRoot<Class>(nullptr);
-}
-
 int String::ComputeHashCode() {
   int32_t hash_code = 0;
   if (IsCompressed()) {
@@ -372,10 +357,6 @@
   return count_diff;
 }
 
-void String::VisitRoots(RootVisitor* visitor) {
-  java_lang_String_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
-}
-
 CharArray* String::ToCharArray(Thread* self) {
   StackHandleScope<1> hs(self);
   Handle<String> string(hs.NewHandle(this));
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 270ace1..598175b 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -228,15 +228,6 @@
         : length;
   }
 
-  static Class* GetJavaLangString() REQUIRES_SHARED(Locks::mutator_lock_) {
-    DCHECK(!java_lang_String_.IsNull());
-    return java_lang_String_.Read();
-  }
-
-  static void SetClass(ObjPtr<Class> java_lang_String) REQUIRES_SHARED(Locks::mutator_lock_);
-  static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
-  static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Returns a human-readable equivalent of 'descriptor'. So "I" would be "int",
   // "[[I" would be "int[][]", "[Ljava/lang/String;" would be
   // "java.lang.String[]", and so forth.
@@ -281,10 +272,7 @@
     uint8_t value_compressed_[0];
   };
 
-  static GcRoot<Class> java_lang_String_;
-
   friend struct art::StringOffsets;  // for verifying offset information
-  ART_FRIEND_TEST(art::StubTest, ReadBarrierForRoot);  // For java_lang_String_.
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(String);
 };
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index 2c12832..cb2d628 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -92,8 +92,7 @@
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     Handle<Class> var_type = hs.NewHandle(view_array_class->GetComponentType());
     Handle<Class> index_type = hs.NewHandle(class_linker->FindPrimitiveClass('I'));
-    ObjPtr<mirror::Class> byte_class = class_linker->FindPrimitiveClass('B');
-    Handle<Class> byte_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &byte_class)));
+    Handle<Class> byte_array_class(hs.NewHandle(GetClassRoot<mirror::ByteArray>()));
     InitializeVarHandle(bvh.Get(), var_type, byte_array_class, index_type, access_modes_bit_mask);
     bvh->SetFieldBoolean<false>(ByteArrayViewVarHandle::NativeByteOrderOffset(), native_byte_order);
     return bvh.Get();
@@ -234,8 +233,7 @@
   ScopedObjectAccess soa(self);
   StackHandleScope<3> hs(self);
   int ptypes_count = static_cast<int>(descriptors.size()) - 1;
-  ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass();
-  ObjPtr<mirror::Class> array_of_class = class_linker->FindArrayClass(self, &class_type);
+  ObjPtr<mirror::Class> array_of_class = GetClassRoot<mirror::ObjectArray<mirror::Class>>();
   Handle<ObjectArray<Class>> ptypes = hs.NewHandle(
       ObjectArray<Class>::Alloc(Thread::Current(), array_of_class, ptypes_count));
   Handle<mirror::ClassLoader> boot_class_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
@@ -599,10 +597,10 @@
                                     VarHandle::AccessMode::kGetAndBitwiseXorRelease,
                                     VarHandle::AccessMode::kGetAndBitwiseXorAcquire);
 
-  ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString();
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  Handle<Class> string_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &string_class)));
-  Handle<mirror::ArrayElementVarHandle> vh(hs.NewHandle(CreateArrayElementVarHandle(self, string_array_class, mask)));
+  Handle<mirror::Class> string_array_class = hs.NewHandle(
+      GetClassRoot<mirror::ObjectArray<mirror::String>>());
+  Handle<mirror::ArrayElementVarHandle> vh(
+      hs.NewHandle(CreateArrayElementVarHandle(self, string_array_class, mask)));
   EXPECT_FALSE(vh.IsNull());
 
   // Check access modes
@@ -746,11 +744,10 @@
                                     VarHandle::AccessMode::kGetAndBitwiseXor,
                                     VarHandle::AccessMode::kGetAndBitwiseXorAcquire);
 
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  ObjPtr<mirror::Class> char_class = class_linker->FindPrimitiveClass('C');
-  Handle<Class> char_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &char_class)));
+  Handle<Class> char_array_class(hs.NewHandle(GetClassRoot<mirror::CharArray>()));
   const bool native_byte_order = true;
-  Handle<mirror::ByteArrayViewVarHandle> vh(hs.NewHandle(CreateByteArrayViewVarHandle(self, char_array_class, native_byte_order, mask)));
+  Handle<mirror::ByteArrayViewVarHandle> vh(
+      hs.NewHandle(CreateByteArrayViewVarHandle(self, char_array_class, native_byte_order, mask)));
   EXPECT_FALSE(vh.IsNull());
   EXPECT_EQ(native_byte_order, vh->GetNativeByteOrder());
 
@@ -895,11 +892,10 @@
                                     VarHandle::AccessMode::kGetAndBitwiseXor,
                                     VarHandle::AccessMode::kGetAndBitwiseXorAcquire);
 
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  ObjPtr<mirror::Class> double_class = class_linker->FindPrimitiveClass('D');
-  Handle<Class> double_array_class(hs.NewHandle(class_linker->FindArrayClass(self, &double_class)));
+  Handle<Class> double_array_class(hs.NewHandle(GetClassRoot<mirror::DoubleArray>()));
   const bool native_byte_order = false;
-  Handle<mirror::ByteBufferViewVarHandle> vh(hs.NewHandle(CreateByteBufferViewVarHandle(self, double_array_class, native_byte_order, mask)));
+  Handle<mirror::ByteBufferViewVarHandle> vh(hs.NewHandle(
+      CreateByteBufferViewVarHandle(self, double_array_class, native_byte_order, mask)));
   EXPECT_FALSE(vh.IsNull());
   EXPECT_EQ(native_byte_order, vh->GetNativeByteOrder());
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 32d9d68..6384d01 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1979,7 +1979,6 @@
   // Visit the classes held as static in mirror classes, these can be visited concurrently and only
   // need to be visited once per GC since they never change.
   mirror::Class::VisitRoots(visitor);
-  mirror::String::VisitRoots(visitor);
   mirror::ClassExt::VisitRoots(visitor);
   // Visiting the roots of these ArtMethods is not currently required since all the GcRoots are
   // null.
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index cbfbb40..73e516c 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -723,6 +723,7 @@
       // mechanics to continue.
       return reg_types->FromUnresolvedMerge(*this, incoming_type, verifier);
     } else {  // Two reference types, compute Join
+      // Do not cache the classes as ClassJoin() can suspend and invalidate ObjPtr<>s.
       DCHECK(GetClass() != nullptr && !GetClass()->IsPrimitive());
       DCHECK(incoming_type.GetClass() != nullptr && !incoming_type.GetClass()->IsPrimitive());
       ObjPtr<mirror::Class> join_class = ClassJoin(GetClass(), incoming_type.GetClass());
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 49db0c8..7ada47d 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -56,9 +56,12 @@
 
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_sleep(JNIEnv*, jobject, jint, jboolean, jdouble) {
   // Keep pausing.
+  struct timespec ts = { .tv_sec = 100, .tv_nsec = 0 };
   printf("Going to sleep\n");
   for (;;) {
-    sleep(1);
+    // Use nanosleep since it gets to the system call quickly and doesn't
+    // have any points at which an unwind will fail.
+    nanosleep(&ts, nullptr);
   }
 }