Remove class root duplicates from well known classes.

And get well known exception classes as declaring classes
of their constructors.

Also change function `ThreadForEnv()` to `Thread::ForEnv()`
and use it where appropriate, mostly in code added recently
while cleaning up well-known methods.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Change-Id: I2ededa429863a6cddbcbb879a223277fd6245557
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 31ce731..d1d3190 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -44,7 +44,7 @@
 #include "oat_quick_method_header.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
-#include "thread.h"
+#include "thread-inl.h"
 
 extern "C" JNIEXPORT jint JNICALL Java_MyClassNatives_bar(JNIEnv*, jobject, jint count) {
   return count + 1;
@@ -407,7 +407,7 @@
 jobject JniCompilerTest::class_loader_;
 
 void JniCompilerTest::AssertCallerObjectLocked(JNIEnv* env) {
-  Thread* self = down_cast<JNIEnvExt*>(env)->GetSelf();
+  Thread* self = Thread::ForEnv(env);
   CHECK_EQ(self, Thread::Current());
   ScopedObjectAccess soa(self);
   ArtMethod** caller_frame = self->GetManagedStack()->GetTopQuickFrame();
diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc
index bb39256..f199ede 100644
--- a/dex2oat/driver/compiler_driver.cc
+++ b/dex2oat/driver/compiler_driver.cc
@@ -1603,8 +1603,7 @@
       mirror::Throwable* exception = soa.Self()->GetException();
       DCHECK(exception != nullptr);
       VLOG(compiler) << "Exception during type resolution: " << exception->Dump();
-      if (exception->GetClass() ==
-              WellKnownClasses::ToClass(WellKnownClasses::java_lang_OutOfMemoryError)) {
+      if (exception->GetClass() == WellKnownClasses::java_lang_OutOfMemoryError.Get()) {
         // There's little point continuing compilation if the heap is exhausted.
         // Trying to do so would also introduce non-deterministic compilation results.
         LOG(FATAL) << "Out of memory during type resolution for compilation";
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index a51e28f..7a68863 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -536,10 +536,9 @@
 static void WrapExceptionInInitializer(Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   Thread* self = Thread::Current();
-  JNIEnv* env = self->GetJniEnv();
 
-  ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
-  CHECK(cause.get() != nullptr);
+  ObjPtr<mirror::Throwable> cause = self->GetException();
+  CHECK(cause != nullptr);
 
   // Boot classpath classes should not fail initialization. This is a consistency debug check.
   // This cannot in general be guaranteed, but in all likelihood leads to breakage down the line.
@@ -554,12 +553,8 @@
                                             << self->GetException()->Dump();
   }
 
-  env->ExceptionClear();
-  bool is_error = env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error);
-  env->Throw(cause.get());
-
   // We only wrap non-Error exceptions; an Error can just be used as-is.
-  if (!is_error) {
+  if (!cause->IsError()) {
     self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", nullptr);
   }
   VlogClassInitializationFailure(klass);
@@ -1132,14 +1127,6 @@
   // classes are always in the boot image, so this code is primarily intended
   // for running without boot image but may be needed for boot image if the
   // AOT-initialization fails due to introduction of new code to `<clinit>`.
-  jclass classes_to_initialize[] = {
-      // Initialize `StackOverflowError`.
-      WellKnownClasses::java_lang_StackOverflowError,
-  };
-  auto* vm = down_cast<JNIEnvExt*>(self->GetJniEnv())->GetVm();
-  for (jclass c : classes_to_initialize) {
-    EnsureRootInitialized(this, self, ObjPtr<mirror::Class>::DownCast(vm->DecodeGlobal(c)));
-  }
   ArtMethod* static_methods_of_classes_to_initialize[] = {
       // Initialize primitive boxing classes (avoid check at runtime).
       WellKnownClasses::java_lang_Boolean_valueOf,
@@ -1150,6 +1137,8 @@
       WellKnownClasses::java_lang_Integer_valueOf,
       WellKnownClasses::java_lang_Long_valueOf,
       WellKnownClasses::java_lang_Short_valueOf,
+      // Initialize `StackOverflowError`.
+      WellKnownClasses::java_lang_StackOverflowError_init,
       // Ensure class loader classes are initialized (avoid check at runtime).
       // Superclass `ClassLoader` is a class root and already initialized above.
       // Superclass `BaseDexClassLoader` is initialized implicitly.
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index d89c5b6..ce9780a 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -24,6 +24,7 @@
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 #include "art_field-inl.h"
+#include "art_method-alloc-inl.h"
 #include "base/dchecked_vector.h"
 #include "base/stl_util.h"
 #include "class_linker.h"
@@ -1362,13 +1363,14 @@
 
 static jobject CreateForeignClassLoader() {
   ScopedObjectAccess soa(Thread::Current());
-  JNIEnv* env = soa.Env();
 
   // We cannot instantiate a ClassLoader directly, so instead we allocate an Object to represent
   // our foreign ClassLoader (this works because the runtime does proper instanceof checks before
   // operating on this object.
-  jmethodID ctor = env->GetMethodID(WellKnownClasses::java_lang_Object, "<init>", "()V");
-  return env->NewObject(WellKnownClasses::java_lang_Object, ctor);
+  ArtMethod* ctor =
+      GetClassRoot<mirror::Object>()->FindClassMethod("<init>", "()V", kRuntimePointerSize);
+  CHECK(ctor != nullptr);
+  return soa.AddLocalReference<jobject>(ctor->NewObject<>(soa.Self()));
 }
 
 TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedBase) {
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 75918a7..5182689 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -714,13 +714,12 @@
     msg += PrettySize(self->GetStackSize());
 
     ScopedObjectAccessUnchecked soa(self);
-    StackHandleScope<2u> hs(self);
-    Handle<mirror::Class> j_l_soe = hs.NewHandle(
-        WellKnownClasses::ToClass(WellKnownClasses::java_lang_StackOverflowError));
-    DCHECK(j_l_soe->IsInitialized());
+    StackHandleScope<1u> hs(self);
 
     // Allocate an uninitialized object.
-    Handle<mirror::Object> exc = hs.NewHandle(j_l_soe->AllocObject(self));
+    DCHECK(WellKnownClasses::java_lang_StackOverflowError->IsInitialized());
+    Handle<mirror::Object> exc = hs.NewHandle(
+        WellKnownClasses::java_lang_StackOverflowError->AllocObject(self));
     if (exc == nullptr) {
       LOG(WARNING) << "Could not allocate StackOverflowError object.";
       return;
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index ba9d48a..8bf3aee 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -581,10 +581,11 @@
     if (kIsDebugBuild && !cc->use_generational_cc_) {
       cc->region_space_->AssertAllRegionLiveBytesZeroOrCleared();
     }
-    if (UNLIKELY(Runtime::Current()->IsActiveTransaction())) {
-      CHECK(Runtime::Current()->IsAotCompiler());
+    Runtime* runtime = Runtime::Current();
+    if (UNLIKELY(runtime->IsActiveTransaction())) {
+      CHECK(runtime->IsAotCompiler());
       TimingLogger::ScopedTiming split3("(Paused)VisitTransactionRoots", cc->GetTimings());
-      Runtime::Current()->VisitTransactionRoots(cc);
+      runtime->VisitTransactionRoots(cc);
     }
     if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
       cc->GrayAllNewlyDirtyImmuneObjects();
@@ -593,15 +594,10 @@
         cc->VerifyGrayImmuneObjects();
       }
     }
-    // May be null during runtime creation, in this case leave java_lang_Object null.
-    // This is safe since single threaded behavior should mean FillWithFakeObject does not
-    // happen when java_lang_Object_ is null.
-    if (WellKnownClasses::java_lang_Object != nullptr) {
-      cc->java_lang_Object_ = down_cast<mirror::Class*>(cc->Mark(thread,
-          WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object).Ptr()));
-    } else {
-      cc->java_lang_Object_ = nullptr;
-    }
+    ObjPtr<mirror::Class> java_lang_Object =
+        GetClassRoot<mirror::Object, kWithoutReadBarrier>(runtime->GetClassLinker());
+    DCHECK(java_lang_Object != nullptr);
+    cc->java_lang_Object_ = down_cast<mirror::Class*>(cc->Mark(thread, java_lang_Object.Ptr()));
   }
 
  private:
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 3bda43d..f3bb166 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -103,6 +103,7 @@
 #include "runtime.h"
 #include "javaheapprof/javaheapsampler.h"
 #include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
 #include "thread_list.h"
 #include "verify_object-inl.h"
 #include "well_known_classes.h"
@@ -4147,7 +4148,7 @@
 // About kNotifyNativeInterval allocations have occurred. Check whether we should garbage collect.
 void Heap::NotifyNativeAllocations(JNIEnv* env) {
   native_objects_notified_.fetch_add(kNotifyNativeInterval, std::memory_order_relaxed);
-  CheckGCForNative(ThreadForEnv(env));
+  CheckGCForNative(Thread::ForEnv(env));
 }
 
 // Register a native allocation with an explicit size.
@@ -4161,7 +4162,7 @@
       native_objects_notified_.fetch_add(1, std::memory_order_relaxed);
   if (objects_notified % kNotifyNativeInterval == kNotifyNativeInterval - 1
       || bytes > kCheckImmediatelyThreshold) {
-    CheckGCForNative(ThreadForEnv(env));
+    CheckGCForNative(Thread::ForEnv(env));
   }
   // Heap profiler treats this as a Java allocation with a null object.
   JHPCheckNonTlabSampleAllocation(Thread::Current(), nullptr, bytes);
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index b538fa9..a38ba1c 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -31,7 +31,7 @@
 #include "object_array.h"
 #include "stack_trace_element-inl.h"
 #include "string.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
 
 namespace art {
 namespace mirror {
@@ -70,14 +70,14 @@
 }
 
 bool Throwable::IsCheckedException() {
-  if (InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error))) {
+  if (IsError()) {
     return false;
   }
-  return !InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_RuntimeException));
+  return !InstanceOf(WellKnownClasses::java_lang_RuntimeException.Get());
 }
 
 bool Throwable::IsError() {
-  return InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error));
+  return InstanceOf(WellKnownClasses::java_lang_Error.Get());
 }
 
 int32_t Throwable::GetStackDepth() {
diff --git a/runtime/native/dalvik_system_BaseDexClassLoader.cc b/runtime/native/dalvik_system_BaseDexClassLoader.cc
index 5c127d0..a4f702c 100644
--- a/runtime/native/dalvik_system_BaseDexClassLoader.cc
+++ b/runtime/native/dalvik_system_BaseDexClassLoader.cc
@@ -23,6 +23,7 @@
 #include "mirror/object_array-alloc-inl.h"
 #include "native_util.h"
 #include "nativehelper/jni_macros.h"
+#include "thread-inl.h"
 
 namespace art {
 
@@ -47,7 +48,7 @@
   CHECK(class_loader != nullptr);
   std::map<std::string, std::string> context_map =
       ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader);
-  Thread* self = down_cast<JNIEnvExt*>(env)->GetSelf();
+  Thread* self = Thread::ForEnv(env);
   ScopedObjectAccess soa(self);
   StackHandleScope<1u> hs(self);
   Handle<mirror::ObjectArray<mirror::String>> array = hs.NewHandle(
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 21e7f77..f2c9efc 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -55,6 +55,7 @@
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "string_array_utils.h"
+#include "thread-current-inl.h"
 
 namespace art {
 
@@ -509,7 +510,7 @@
   }
 
   // Now create output array and copy the set into it.
-  ScopedObjectAccess soa(down_cast<JNIEnvExt*>(env)->GetSelf());
+  ScopedObjectAccess soa(Thread::ForEnv(env));
   auto descriptor_to_dot = [](const char* descriptor) { return DescriptorToDot(descriptor); };
   return soa.AddLocalReference<jobjectArray>(CreateStringArray(
       soa.Self(),
@@ -645,7 +646,7 @@
   OatFileAssistant::GetOptimizationStatus(
       filename.c_str(), target_instruction_set, &compilation_filter, &compilation_reason);
 
-  ScopedObjectAccess soa(down_cast<JNIEnvExt*>(env)->GetSelf());
+  ScopedObjectAccess soa(Thread::ForEnv(env));
   return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), {
       compilation_filter.c_str(),
       compilation_reason.c_str()
@@ -900,7 +901,7 @@
     filenames[1] = vdex_filename.c_str();
     used_filenames = ArrayRef<const char* const>(filenames, 2u);
   }
-  ScopedObjectAccess soa(down_cast<JNIEnvExt*>(env)->GetSelf());
+  ScopedObjectAccess soa(Thread::ForEnv(env));
   return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), used_filenames));
 }
 
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 2c651df..d44fcc8 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -48,14 +48,14 @@
 #include "nativehelper/scoped_utf_chars.h"
 #include "scoped_fast_native_object_access-inl.h"
 #include "string_array_utils.h"
+#include "thread-inl.h"
 #include "trace.h"
 
 namespace art {
 
 static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) {
-  Thread* self = down_cast<JNIEnvExt*>(env)->GetSelf();
-  ScopedObjectAccess soa(self);
-  return soa.AddLocalReference<jobjectArray>(CreateStringArray(self, {
+  ScopedObjectAccess soa(Thread::ForEnv(env));
+  return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), {
       "method-trace-profiling",
       "method-trace-profiling-streaming",
       "method-sample-profiling",
@@ -380,7 +380,7 @@
 }
 
 static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) {
-  Thread* self = down_cast<JNIEnvExt*>(env)->GetSelf();
+  Thread* self = Thread::ForEnv(env);
   ScopedObjectAccess soa(self);
   StackHandleScope<1u> hs(self);
   int32_t size = enum_cast<int32_t>(VMDebugRuntimeStatId::kNumRuntimeStats);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index a881879..74202fc 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -62,7 +62,7 @@
 #include "scoped_fast_native_object_access-inl.h"
 #include "scoped_thread_state_change-inl.h"
 #include "string_array_utils.h"
-#include "thread.h"
+#include "thread-inl.h"
 #include "thread_list.h"
 
 namespace art {
@@ -191,7 +191,7 @@
 
 static jobjectArray VMRuntime_properties(JNIEnv* env, jobject) {
   const std::vector<std::string>& properties = Runtime::Current()->GetProperties();
-  ScopedObjectAccess soa(down_cast<JNIEnvExt*>(env)->GetSelf());
+  ScopedObjectAccess soa(Thread::ForEnv(env));
   return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), properties));
 }
 
@@ -327,31 +327,31 @@
 }
 
 static void VMRuntime_trimHeap(JNIEnv* env, jobject) {
-  Runtime::Current()->GetHeap()->Trim(ThreadForEnv(env));
+  Runtime::Current()->GetHeap()->Trim(Thread::ForEnv(env));
 }
 
 static void VMRuntime_requestHeapTrim(JNIEnv* env, jobject) {
-  Runtime::Current()->GetHeap()->RequestTrim(ThreadForEnv(env));
+  Runtime::Current()->GetHeap()->RequestTrim(Thread::ForEnv(env));
 }
 
 static void VMRuntime_requestConcurrentGC(JNIEnv* env, jobject) {
   gc::Heap *heap = Runtime::Current()->GetHeap();
-  heap->RequestConcurrentGC(ThreadForEnv(env),
+  heap->RequestConcurrentGC(Thread::ForEnv(env),
                             gc::kGcCauseBackground,
                             true,
                             heap->GetCurrentGcNum());
 }
 
 static void VMRuntime_startHeapTaskProcessor(JNIEnv* env, jobject) {
-  Runtime::Current()->GetHeap()->GetTaskProcessor()->Start(ThreadForEnv(env));
+  Runtime::Current()->GetHeap()->GetTaskProcessor()->Start(Thread::ForEnv(env));
 }
 
 static void VMRuntime_stopHeapTaskProcessor(JNIEnv* env, jobject) {
-  Runtime::Current()->GetHeap()->GetTaskProcessor()->Stop(ThreadForEnv(env));
+  Runtime::Current()->GetHeap()->GetTaskProcessor()->Stop(Thread::ForEnv(env));
 }
 
 static void VMRuntime_runHeapTasks(JNIEnv* env, jobject) {
-  Runtime::Current()->GetHeap()->GetTaskProcessor()->RunAllTasks(ThreadForEnv(env));
+  Runtime::Current()->GetHeap()->GetTaskProcessor()->RunAllTasks(Thread::ForEnv(env));
 }
 
 static void VMRuntime_preloadDexCaches(JNIEnv* env ATTRIBUTE_UNUSED, jobject) {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 15188e9..f7d7d80 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -257,7 +257,7 @@
   runtime->PreZygoteFork();
 
   // Grab thread before fork potentially makes Thread::pthread_key_self_ unusable.
-  return reinterpret_cast<jlong>(ThreadForEnv(env));
+  return reinterpret_cast<jlong>(Thread::ForEnv(env));
 }
 
 static void ZygoteHooks_nativePostZygoteFork(JNIEnv*, jclass) {
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index b327e51..b9c72b8 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -35,7 +35,8 @@
 #include "obj_ptr.h"
 #include "scoped_fast_native_object_access-inl.h"
 #include "string_array_utils.h"
-#include "well_known_classes.h"
+#include "thread-inl.h"
+#include "well_known_classes-inl.h"
 
 namespace art {
 
@@ -96,12 +97,9 @@
   if (c != nullptr && c->IsErroneous()) {
     cl->ThrowEarlierClassFailure(c);
     Thread* self = soa.Self();
-    ObjPtr<mirror::Class> iae_class =
-        self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass();
-    ObjPtr<mirror::Class> ncdfe_class =
-        self->DecodeJObject(WellKnownClasses::java_lang_NoClassDefFoundError)->AsClass();
-    ObjPtr<mirror::Class> exception = self->GetException()->GetClass();
-    if (exception == iae_class || exception == ncdfe_class) {
+    ObjPtr<mirror::Class> exception_class = self->GetException()->GetClass();
+    if (exception_class == WellKnownClasses::java_lang_IllegalAccessError ||
+        exception_class == WellKnownClasses::java_lang_NoClassDefFoundError) {
       self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;",
                                      c->PrettyDescriptor().c_str());
     }
@@ -159,7 +157,7 @@
     return is_base_dex(dex_file);
   };
   auto get_location = [](const DexFile* dex_file) { return dex_file->GetLocation(); };
-  ScopedObjectAccess soa(down_cast<JNIEnvExt*>(env)->GetSelf());
+  ScopedObjectAccess soa(Thread::ForEnv(env));
   return soa.AddLocalReference<jobjectArray>(CreateStringArray(
       soa.Self(),
       jar_count,
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index f25d446..0560223 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -2166,20 +2166,26 @@
   // By setting calling class to java.lang.Object, the caller location for these
   // JNI libs is core-oj.jar in the ART APEX, and hence they are loaded from the
   // com_android_art linker namespace.
+  jclass java_lang_Object;
+  {
+    ScopedObjectAccess soa(self);
+    java_lang_Object = reinterpret_cast<jclass>(
+        GetJavaVM()->AddGlobalRef(self, GetClassRoot<mirror::Object>(GetClassLinker())));
+  }
 
   // libicu_jni has to be initialized before libopenjdk{d} due to runtime dependency from
   // libopenjdk{d} to Icu4cMetadata native methods in libicu_jni. See http://b/143888405
   {
     std::string error_msg;
     if (!java_vm_->LoadNativeLibrary(
-          env, "libicu_jni.so", nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
+          env, "libicu_jni.so", nullptr, java_lang_Object, &error_msg)) {
       LOG(FATAL) << "LoadNativeLibrary failed for \"libicu_jni.so\": " << error_msg;
     }
   }
   {
     std::string error_msg;
     if (!java_vm_->LoadNativeLibrary(
-          env, "libjavacore.so", nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
+          env, "libjavacore.so", nullptr, java_lang_Object, &error_msg)) {
       LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg;
     }
   }
@@ -2189,10 +2195,11 @@
                                                 : "libopenjdk.so";
     std::string error_msg;
     if (!java_vm_->LoadNativeLibrary(
-          env, kOpenJdkLibrary, nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
+          env, kOpenJdkLibrary, nullptr, java_lang_Object, &error_msg)) {
       LOG(FATAL) << "LoadNativeLibrary failed for \"" << kOpenJdkLibrary << "\": " << error_msg;
     }
   }
+  env->DeleteGlobalRef(java_lang_Object);
 
   // Initialize well known classes that may invoke runtime native methods.
   WellKnownClasses::LateInit(env);
diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h
index d601952..674d791 100644
--- a/runtime/scoped_thread_state_change-inl.h
+++ b/runtime/scoped_thread_state_change-inl.h
@@ -94,7 +94,7 @@
 }
 
 inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(JNIEnv* env)
-    : self_(ThreadForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->GetVm()) {}
+    : self_(Thread::ForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->GetVm()) {}
 
 inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(Thread* self)
     : self_(self),
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 4110ed2..a99977f 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -34,7 +34,7 @@
 namespace art {
 
 // Quickly access the current thread from a JNIEnv.
-static inline Thread* ThreadForEnv(JNIEnv* env) {
+inline Thread* Thread::ForEnv(JNIEnv* env) {
   JNIEnvExt* full_env(down_cast<JNIEnvExt*>(env));
   return full_env->GetSelf();
 }
diff --git a/runtime/thread.cc b/runtime/thread.cc
index da403b5..a8b0e17 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2495,8 +2495,7 @@
 void Thread::AssertPendingOOMException() const {
   AssertPendingException();
   auto* e = GetException();
-  CHECK_EQ(e->GetClass(), DecodeJObject(WellKnownClasses::java_lang_OutOfMemoryError)->AsClass())
-      << e->Dump();
+  CHECK_EQ(e->GetClass(), WellKnownClasses::java_lang_OutOfMemoryError.Get()) << e->Dump();
 }
 
 void Thread::AssertNoPendingException() const {
diff --git a/runtime/thread.h b/runtime/thread.h
index e6b419d..2613b15 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -248,6 +248,9 @@
   // TODO: mark as PURE so the compiler may coalesce and remove?
   static Thread* Current();
 
+  // Get the thread from the JNI environment.
+  static Thread* ForEnv(JNIEnv* env);
+
   // On a runnable thread, check for pending thread suspension request and handle if pending.
   void AllowThreadSuspension() REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 771df58..397f2bd 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -49,16 +49,8 @@
 jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
 jclass WellKnownClasses::dalvik_annotation_optimization_NeverCompile;
 jclass WellKnownClasses::dalvik_annotation_optimization_NeverInline;
-jclass WellKnownClasses::dalvik_system_EmulatedStackFrame;
 jclass WellKnownClasses::java_lang_annotation_Annotation__array;
-jclass WellKnownClasses::java_lang_Error;
-jclass WellKnownClasses::java_lang_IllegalAccessError;
-jclass WellKnownClasses::java_lang_NoClassDefFoundError;
-jclass WellKnownClasses::java_lang_Object;
-jclass WellKnownClasses::java_lang_OutOfMemoryError;
 jclass WellKnownClasses::java_lang_reflect_Parameter__array;
-jclass WellKnownClasses::java_lang_RuntimeException;
-jclass WellKnownClasses::java_lang_StackOverflowError;
 jclass WellKnownClasses::java_lang_StringFactory;
 jclass WellKnownClasses::java_lang_System;
 jclass WellKnownClasses::java_lang_Void;
@@ -81,28 +73,34 @@
 ArtMethod* WellKnownClasses::java_lang_Daemons_waitForDaemonStart;
 ArtMethod* WellKnownClasses::java_lang_Double_doubleToRawLongBits;
 ArtMethod* WellKnownClasses::java_lang_Double_valueOf;
+ArtMethod* WellKnownClasses::java_lang_Error_init;
 ArtMethod* WellKnownClasses::java_lang_Float_floatToRawIntBits;
 ArtMethod* WellKnownClasses::java_lang_Float_valueOf;
+ArtMethod* WellKnownClasses::java_lang_IllegalAccessError_init;
 ArtMethod* WellKnownClasses::java_lang_Integer_valueOf;
-ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandle_asType;
-ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact;
-ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandles_lookup;
-ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor;
 ArtMethod* WellKnownClasses::java_lang_Long_valueOf;
-ArtMethod* WellKnownClasses::java_lang_ref_FinalizerReference_add;
-ArtMethod* WellKnownClasses::java_lang_ref_ReferenceQueue_add;
-ArtMethod* WellKnownClasses::java_lang_reflect_InvocationTargetException_init;
-ArtMethod* WellKnownClasses::java_lang_reflect_Parameter_init;
-ArtMethod* WellKnownClasses::java_lang_reflect_Proxy_init;
-ArtMethod* WellKnownClasses::java_lang_reflect_Proxy_invoke;
+ArtMethod* WellKnownClasses::java_lang_NoClassDefFoundError_init;
+ArtMethod* WellKnownClasses::java_lang_OutOfMemoryError_init;
 ArtMethod* WellKnownClasses::java_lang_Runtime_nativeLoad;
+ArtMethod* WellKnownClasses::java_lang_RuntimeException_init;
 ArtMethod* WellKnownClasses::java_lang_Short_valueOf;
+ArtMethod* WellKnownClasses::java_lang_StackOverflowError_init;
 ArtMethod* WellKnownClasses::java_lang_String_charAt;
 ArtMethod* WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
 ArtMethod* WellKnownClasses::java_lang_Thread_init;
 ArtMethod* WellKnownClasses::java_lang_Thread_run;
 ArtMethod* WellKnownClasses::java_lang_ThreadGroup_add;
 ArtMethod* WellKnownClasses::java_lang_ThreadGroup_threadTerminated;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandle_asType;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandles_lookup;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor;
+ArtMethod* WellKnownClasses::java_lang_ref_FinalizerReference_add;
+ArtMethod* WellKnownClasses::java_lang_ref_ReferenceQueue_add;
+ArtMethod* WellKnownClasses::java_lang_reflect_InvocationTargetException_init;
+ArtMethod* WellKnownClasses::java_lang_reflect_Parameter_init;
+ArtMethod* WellKnownClasses::java_lang_reflect_Proxy_init;
+ArtMethod* WellKnownClasses::java_lang_reflect_Proxy_invoke;
 ArtMethod* WellKnownClasses::java_nio_Buffer_isDirect;
 ArtMethod* WellKnownClasses::java_nio_DirectByteBuffer_init;
 ArtMethod* WellKnownClasses::java_util_function_Consumer_accept;
@@ -318,17 +316,9 @@
       CacheClass(env, "dalvik/annotation/optimization/NeverCompile");
   dalvik_annotation_optimization_NeverInline =
       CacheClass(env, "dalvik/annotation/optimization/NeverInline");
-  dalvik_system_EmulatedStackFrame = CacheClass(env, "dalvik/system/EmulatedStackFrame");
 
   java_lang_annotation_Annotation__array = CacheClass(env, "[Ljava/lang/annotation/Annotation;");
-  java_lang_Object = CacheClass(env, "java/lang/Object");
-  java_lang_OutOfMemoryError = CacheClass(env, "java/lang/OutOfMemoryError");
-  java_lang_Error = CacheClass(env, "java/lang/Error");
-  java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError");
-  java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError");
   java_lang_reflect_Parameter__array = CacheClass(env, "[Ljava/lang/reflect/Parameter;");
-  java_lang_RuntimeException = CacheClass(env, "java/lang/RuntimeException");
-  java_lang_StackOverflowError = CacheClass(env, "java/lang/StackOverflowError");
   java_lang_StringFactory = CacheClass(env, "java/lang/StringFactory");
   java_lang_System = CacheClass(env, "java/lang/System");
   java_lang_Void = CacheClass(env, "java/lang/Void");
@@ -341,7 +331,7 @@
   hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
       hiddenapi::EnforcementPolicy::kDisabled);
 
-  Thread* self = down_cast<JNIEnvExt*>(env)->GetSelf();
+  Thread* self = Thread::ForEnv(env);
   ScopedObjectAccess soa(self);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
 
@@ -362,7 +352,7 @@
   java_lang_Short_valueOf =
       CachePrimitiveBoxingMethod(class_linker, self, 'S', "Ljava/lang/Short;");
 
-  StackHandleScope<33u> hs(self);
+  StackHandleScope<39u> hs(self);
   Handle<mirror::Class> d_s_bdcl =
       hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/BaseDexClassLoader;"));
   Handle<mirror::Class> d_s_dlcl =
@@ -391,6 +381,18 @@
       hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ClassNotFoundException;"));
   Handle<mirror::Class> j_l_Daemons =
       hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Daemons;"));
+  Handle<mirror::Class> j_l_Error =
+      hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Error;"));
+  Handle<mirror::Class> j_l_IllegalAccessError =
+      hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/IllegalAccessError;"));
+  Handle<mirror::Class> j_l_NoClassDefFoundError =
+      hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/NoClassDefFoundError;"));
+  Handle<mirror::Class> j_l_OutOfMemoryError =
+      hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/OutOfMemoryError;"));
+  Handle<mirror::Class> j_l_RuntimeException =
+      hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/RuntimeException;"));
+  Handle<mirror::Class> j_l_StackOverflowError =
+      hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/StackOverflowError;"));
   Handle<mirror::Class> j_l_Thread =
       hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Thread;"));
   Handle<mirror::Class> j_l_tg =
@@ -501,6 +503,19 @@
   java_lang_Daemons_waitForDaemonStart = CacheMethod(
       j_l_Daemons.Get(), /*is_static=*/ true, "waitForDaemonStart", "()V", pointer_size);
 
+  java_lang_Error_init = CacheMethod(
+      j_l_Error.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+  java_lang_IllegalAccessError_init = CacheMethod(
+      j_l_IllegalAccessError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+  java_lang_NoClassDefFoundError_init = CacheMethod(
+      j_l_NoClassDefFoundError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+  java_lang_OutOfMemoryError_init = CacheMethod(
+      j_l_OutOfMemoryError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+  java_lang_RuntimeException_init = CacheMethod(
+      j_l_RuntimeException.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+  java_lang_StackOverflowError_init = CacheMethod(
+      j_l_StackOverflowError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+
   ObjPtr<mirror::Class> j_l_String = GetClassRoot<mirror::String>(class_linker);
   java_lang_String_charAt = CacheMethod(
       j_l_String, /*is_static=*/ false, "charAt", "(I)C", pointer_size);
@@ -715,7 +730,7 @@
   // by `CacheMethod()` calling `FindMethodJNI()`.
   // TODO: Move this initialization to `ClassLinker`.
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  Thread* self = down_cast<JNIEnvExt*>(env)->GetSelf();
+  Thread* self = Thread::ForEnv(env);
   ScopedObjectAccess soa(self);
   StackHandleScope<1u> hs(self);
   Handle<mirror::Class> j_l_Runtime =
@@ -749,16 +764,8 @@
   dalvik_annotation_optimization_FastNative = nullptr;
   dalvik_annotation_optimization_NeverCompile = nullptr;
   dalvik_annotation_optimization_NeverInline = nullptr;
-  dalvik_system_EmulatedStackFrame = nullptr;
   java_lang_annotation_Annotation__array = nullptr;
-  java_lang_Error = nullptr;
-  java_lang_IllegalAccessError = nullptr;
-  java_lang_NoClassDefFoundError = nullptr;
-  java_lang_Object = nullptr;
-  java_lang_OutOfMemoryError = nullptr;
   java_lang_reflect_Parameter__array = nullptr;
-  java_lang_RuntimeException = nullptr;
-  java_lang_StackOverflowError = nullptr;
   java_lang_StringFactory = nullptr;
   java_lang_System = nullptr;
   java_lang_Void = nullptr;
@@ -782,28 +789,34 @@
   java_lang_Daemons_waitForDaemonStart = nullptr;
   java_lang_Double_doubleToRawLongBits = nullptr;
   java_lang_Double_valueOf = nullptr;
+  java_lang_Error_init = nullptr;
   java_lang_Float_floatToRawIntBits = nullptr;
   java_lang_Float_valueOf = nullptr;
+  java_lang_IllegalAccessError_init = nullptr;
   java_lang_Integer_valueOf = nullptr;
-  java_lang_invoke_MethodHandle_asType = nullptr;
-  java_lang_invoke_MethodHandle_invokeExact = nullptr;
-  java_lang_invoke_MethodHandles_lookup = nullptr;
-  java_lang_invoke_MethodHandles_Lookup_findConstructor = nullptr;
   java_lang_Long_valueOf = nullptr;
-  java_lang_ref_FinalizerReference_add = nullptr;
-  java_lang_ref_ReferenceQueue_add = nullptr;
-  java_lang_reflect_InvocationTargetException_init = nullptr;
-  java_lang_reflect_Parameter_init = nullptr;
-  java_lang_reflect_Proxy_init = nullptr;
-  java_lang_reflect_Proxy_invoke = nullptr;
+  java_lang_NoClassDefFoundError_init = nullptr;
+  java_lang_OutOfMemoryError_init = nullptr;
   java_lang_Runtime_nativeLoad = nullptr;
+  java_lang_RuntimeException_init = nullptr;
   java_lang_Short_valueOf = nullptr;
+  java_lang_StackOverflowError_init = nullptr;
   java_lang_String_charAt = nullptr;
   java_lang_Thread_dispatchUncaughtException = nullptr;
   java_lang_Thread_init = nullptr;
   java_lang_Thread_run = nullptr;
   java_lang_ThreadGroup_add = nullptr;
   java_lang_ThreadGroup_threadTerminated = nullptr;
+  java_lang_invoke_MethodHandle_asType = nullptr;
+  java_lang_invoke_MethodHandle_invokeExact = nullptr;
+  java_lang_invoke_MethodHandles_lookup = nullptr;
+  java_lang_invoke_MethodHandles_Lookup_findConstructor = nullptr;
+  java_lang_ref_FinalizerReference_add = nullptr;
+  java_lang_ref_ReferenceQueue_add = nullptr;
+  java_lang_reflect_InvocationTargetException_init = nullptr;
+  java_lang_reflect_Parameter_init = nullptr;
+  java_lang_reflect_Proxy_init = nullptr;
+  java_lang_reflect_Proxy_invoke = nullptr;
   java_nio_Buffer_isDirect = nullptr;
   java_nio_DirectByteBuffer_init = nullptr;
   libcore_reflect_AnnotationFactory_createAnnotation = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 56e49db..e8af1c1 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -94,16 +94,8 @@
   static jclass dalvik_annotation_optimization_FastNative;
   static jclass dalvik_annotation_optimization_NeverCompile;
   static jclass dalvik_annotation_optimization_NeverInline;
-  static jclass dalvik_system_EmulatedStackFrame;
   static jclass java_lang_annotation_Annotation__array;
-  static jclass java_lang_Error;
-  static jclass java_lang_IllegalAccessError;
-  static jclass java_lang_NoClassDefFoundError;
-  static jclass java_lang_Object;
-  static jclass java_lang_OutOfMemoryError;
   static jclass java_lang_reflect_Parameter__array;
-  static jclass java_lang_RuntimeException;
-  static jclass java_lang_StackOverflowError;
   static jclass java_lang_StringFactory;
   static jclass java_lang_System;
   static jclass java_lang_Void;
@@ -126,28 +118,34 @@
   static ArtMethod* java_lang_Daemons_waitForDaemonStart;
   static ArtMethod* java_lang_Double_doubleToRawLongBits;
   static ArtMethod* java_lang_Double_valueOf;
+  static ArtMethod* java_lang_Error_init;  // Only for the declaring class.
   static ArtMethod* java_lang_Float_floatToRawIntBits;
   static ArtMethod* java_lang_Float_valueOf;
+  static ArtMethod* java_lang_IllegalAccessError_init;  // Only for the declaring class.
   static ArtMethod* java_lang_Integer_valueOf;
-  static ArtMethod* java_lang_invoke_MethodHandle_asType;
-  static ArtMethod* java_lang_invoke_MethodHandle_invokeExact;
-  static ArtMethod* java_lang_invoke_MethodHandles_lookup;
-  static ArtMethod* java_lang_invoke_MethodHandles_Lookup_findConstructor;
   static ArtMethod* java_lang_Long_valueOf;
-  static ArtMethod* java_lang_ref_FinalizerReference_add;
-  static ArtMethod* java_lang_ref_ReferenceQueue_add;
-  static ArtMethod* java_lang_reflect_InvocationTargetException_init;
-  static ArtMethod* java_lang_reflect_Parameter_init;
-  static ArtMethod* java_lang_reflect_Proxy_init;
-  static ArtMethod* java_lang_reflect_Proxy_invoke;
+  static ArtMethod* java_lang_NoClassDefFoundError_init;  // Only for the declaring class.
+  static ArtMethod* java_lang_OutOfMemoryError_init;  // Only for the declaring class.
   static ArtMethod* java_lang_Runtime_nativeLoad;
+  static ArtMethod* java_lang_RuntimeException_init;  // Only for the declaring class.
   static ArtMethod* java_lang_Short_valueOf;
+  static ArtMethod* java_lang_StackOverflowError_init;  // Only for the declaring class.
   static ArtMethod* java_lang_String_charAt;
   static ArtMethod* java_lang_Thread_dispatchUncaughtException;
   static ArtMethod* java_lang_Thread_init;
   static ArtMethod* java_lang_Thread_run;
   static ArtMethod* java_lang_ThreadGroup_add;
   static ArtMethod* java_lang_ThreadGroup_threadTerminated;
+  static ArtMethod* java_lang_invoke_MethodHandle_asType;
+  static ArtMethod* java_lang_invoke_MethodHandle_invokeExact;
+  static ArtMethod* java_lang_invoke_MethodHandles_lookup;
+  static ArtMethod* java_lang_invoke_MethodHandles_Lookup_findConstructor;
+  static ArtMethod* java_lang_ref_FinalizerReference_add;
+  static ArtMethod* java_lang_ref_ReferenceQueue_add;
+  static ArtMethod* java_lang_reflect_InvocationTargetException_init;
+  static ArtMethod* java_lang_reflect_Parameter_init;
+  static ArtMethod* java_lang_reflect_Proxy_init;
+  static ArtMethod* java_lang_reflect_Proxy_invoke;
   static ArtMethod* java_nio_Buffer_isDirect;
   static ArtMethod* java_nio_DirectByteBuffer_init;
   static ArtMethod* java_util_function_Consumer_accept;
@@ -219,6 +217,15 @@
   static constexpr ClassFromMethod<&java_lang_BootClassLoader_init> java_lang_BootClassLoader;
   static constexpr ClassFromField<&java_lang_ClassLoader_parent> java_lang_ClassLoader;
   static constexpr ClassFromMethod<&java_lang_Daemons_start> java_lang_Daemons;
+  static constexpr ClassFromMethod<&java_lang_Error_init> java_lang_Error;
+  static constexpr ClassFromMethod<&java_lang_IllegalAccessError_init>
+      java_lang_IllegalAccessError;
+  static constexpr ClassFromMethod<&java_lang_NoClassDefFoundError_init>
+      java_lang_NoClassDefFoundError;
+  static constexpr ClassFromMethod<&java_lang_OutOfMemoryError_init> java_lang_OutOfMemoryError;
+  static constexpr ClassFromMethod<&java_lang_RuntimeException_init> java_lang_RuntimeException;
+  static constexpr ClassFromMethod<&java_lang_StackOverflowError_init>
+      java_lang_StackOverflowError;
   static constexpr ClassFromField<&java_lang_Thread_daemon> java_lang_Thread;
   static constexpr ClassFromField<&java_lang_ThreadGroup_groups> java_lang_ThreadGroup;
   static constexpr ClassFromMethod<&java_lang_reflect_InvocationTargetException_init>
diff --git a/test/051-thread/thread_test.cc b/test/051-thread/thread_test.cc
index 079ad40..33841eb 100644
--- a/test/051-thread/thread_test.cc
+++ b/test/051-thread/thread_test.cc
@@ -22,7 +22,7 @@
 
 extern "C" JNIEXPORT jint JNICALL Java_Main_getNativePriority(JNIEnv* env,
                                                               jclass clazz ATTRIBUTE_UNUSED) {
-  return ThreadForEnv(env)->GetNativePriority();
+  return Thread::ForEnv(env)->GetNativePriority();
 }
 
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportsThreadPriorities(