Revert "Revert "Unload oat files""

Fixed a race where two threads calling OatFile::Open could both use
dlopen on the host.

Bug: 22720414

This reverts commit 72da5e7461fec3b1e116050f2e6f233efb9c54f3.

Change-Id: I1636045b724944d2a09417527280784967957095
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 30bfb4a..70bd398 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -51,6 +51,7 @@
 MutatorMutex* Locks::mutator_lock_ = nullptr;
 Mutex* Locks::profiler_lock_ = nullptr;
 ReaderWriterMutex* Locks::oat_file_manager_lock_ = nullptr;
+ReaderWriterMutex* Locks::oat_file_count_lock_ = nullptr;
 Mutex* Locks::reference_processor_lock_ = nullptr;
 Mutex* Locks::reference_queue_cleared_references_lock_ = nullptr;
 Mutex* Locks::reference_queue_finalizer_references_lock_ = nullptr;
@@ -942,6 +943,7 @@
     DCHECK(deoptimization_lock_ != nullptr);
     DCHECK(heap_bitmap_lock_ != nullptr);
     DCHECK(oat_file_manager_lock_ != nullptr);
+    DCHECK(oat_file_count_lock_ != nullptr);
     DCHECK(intern_table_lock_ != nullptr);
     DCHECK(jni_libraries_lock_ != nullptr);
     DCHECK(logging_lock_ != nullptr);
@@ -1034,6 +1036,10 @@
     DCHECK(oat_file_manager_lock_ == nullptr);
     oat_file_manager_lock_ = new ReaderWriterMutex("OatFile manager lock", current_lock_level);
 
+    UPDATE_CURRENT_LOCK_LEVEL(kOatFileCountLock);
+    DCHECK(oat_file_count_lock_ == nullptr);
+    oat_file_count_lock_ = new ReaderWriterMutex("OatFile count lock", current_lock_level);
+
     UPDATE_CURRENT_LOCK_LEVEL(kInternTableLock);
     DCHECK(intern_table_lock_ == nullptr);
     intern_table_lock_ = new Mutex("InternTable lock", current_lock_level);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 17f6a03..d4c9057 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -83,6 +83,7 @@
   kDexFileToMethodInlinerMapLock,
   kInternTableLock,
   kOatFileSecondaryLookupLock,
+  kOatFileCountLock,
   kOatFileManagerLock,
   kTracingUniqueMethodsLock,
   kTracingStreamingLock,
@@ -648,8 +649,11 @@
   // Guards opened oat files in OatFileManager.
   static ReaderWriterMutex* oat_file_manager_lock_ ACQUIRED_AFTER(modify_ldt_lock_);
 
+  // Guards opened oat files in OatFileManager.
+  static ReaderWriterMutex* oat_file_count_lock_ ACQUIRED_AFTER(oat_file_manager_lock_);
+
   // Guards intern table.
-  static Mutex* intern_table_lock_ ACQUIRED_AFTER(oat_file_manager_lock_);
+  static Mutex* intern_table_lock_ ACQUIRED_AFTER(oat_file_count_lock_);
 
   // Guards reference processor.
   static Mutex* reference_processor_lock_ ACQUIRED_AFTER(intern_table_lock_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9349fe3..370ea5c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -41,25 +41,20 @@
 #include "compiler_callbacks.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
+#include "entrypoints/entrypoint_utils.h"
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc_root-inl.h"
 #include "gc/accounting/card_table-inl.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "gc/heap.h"
 #include "gc/space/image_space.h"
-#include "handle_scope.h"
+#include "handle_scope-inl.h"
 #include "intern_table.h"
 #include "interpreter/interpreter.h"
 #include "jit/jit.h"
 #include "jit/jit_code_cache.h"
 #include "leb128.h"
 #include "linear_alloc.h"
-#include "oat.h"
-#include "oat_file.h"
-#include "oat_file-inl.h"
-#include "oat_file_assistant.h"
-#include "oat_file_manager.h"
-#include "object_lock.h"
 #include "mirror/class.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
@@ -73,12 +68,17 @@
 #include "mirror/reference-inl.h"
 #include "mirror/stack_trace_element.h"
 #include "mirror/string-inl.h"
+#include "native/dalvik_system_DexFile.h"
+#include "oat.h"
+#include "oat_file.h"
+#include "oat_file-inl.h"
+#include "oat_file_assistant.h"
+#include "oat_file_manager.h"
+#include "object_lock.h"
 #include "os.h"
 #include "runtime.h"
-#include "entrypoints/entrypoint_utils.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
-#include "handle_scope-inl.h"
 #include "thread-inl.h"
 #include "trace.h"
 #include "utils.h"
@@ -1429,13 +1429,18 @@
             break;
           }
           int32_t long_array_size = long_array->GetLength();
-          for (int32_t j = 0; j < long_array_size; ++j) {
+          // First element is the oat file.
+          for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
             const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
                 long_array->GetWithoutChecks(j)));
             const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash);
             if (dex_class_def != nullptr) {
-              mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader,
-                                                 *cp_dex_file, *dex_class_def);
+              mirror::Class* klass = DefineClass(self,
+                                                 descriptor,
+                                                 hash,
+                                                 class_loader,
+                                                 *cp_dex_file,
+                                                 *dex_class_def);
               if (klass == nullptr) {
                 CHECK(self->IsExceptionPending()) << descriptor;
                 self->ClearException();
@@ -5794,9 +5799,13 @@
   for (const DexFile* dex_file : dex_files) {
     StackHandleScope<3> hs2(self);
 
-    Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(self, 1));
+    // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the
+    // oat file but we can leave it null.
+    Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(
+        self,
+        kDexFileIndexStart + 1));
     DCHECK(h_long_array.Get() != nullptr);
-    h_long_array->Set(0, reinterpret_cast<intptr_t>(dex_file));
+    h_long_array->Set(kDexFileIndexStart, reinterpret_cast<intptr_t>(dex_file));
 
     Handle<mirror::Object> h_dex_file = hs2.NewHandle(
         cookie_field->GetDeclaringClass()->AllocObject(self));
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 56c5d1a..b6b5141 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -42,6 +42,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mem_map.h"
+#include "native/dalvik_system_DexFile.h"
 #include "noop_compiler_callbacks.h"
 #include "os.h"
 #include "primitive.h"
@@ -516,7 +517,7 @@
           mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray();
           DCHECK(long_array != nullptr);
           int32_t long_array_size = long_array->GetLength();
-          for (int32_t j = 0; j < long_array_size; ++j) {
+          for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
             const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
                 long_array->GetWithoutChecks(j)));
             if (cp_dex_file == nullptr) {
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 2a019c5..90cc189 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -135,10 +135,21 @@
 #endif
 
 // Return true if the address range is contained in a single /proc/self/map entry.
-static bool ContainedWithinExistingMap(uint8_t* ptr, size_t size,
-                                       std::string* error_msg) {
+bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* error_msg) {
   uintptr_t begin = reinterpret_cast<uintptr_t>(ptr);
   uintptr_t end = begin + size;
+
+  {
+    MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
+    for (auto& pair : *MemMap::maps_) {
+      MemMap* const map = pair.second;
+      if (begin >= reinterpret_cast<uintptr_t>(map->Begin()) &&
+          end <= reinterpret_cast<uintptr_t>(map->End())) {
+        return true;
+      }
+    }
+  }
+
   std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
   if (map.get() == nullptr) {
     *error_msg = StringPrintf("Failed to build process map");
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index 196a7f6..7c11ceb 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -161,6 +161,8 @@
       REQUIRES(Locks::mem_maps_lock_);
   static MemMap* GetLargestMemMapAt(void* address)
       REQUIRES(Locks::mem_maps_lock_);
+  static bool ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* error_msg)
+      REQUIRES(!Locks::mem_maps_lock_);
 
   const std::string name_;
   uint8_t* const begin_;  // Start of data.
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 4850b6f..1a6bead 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -40,13 +40,16 @@
 
 namespace art {
 
-static std::unique_ptr<std::vector<const DexFile*>>
-ConvertJavaArrayToNative(JNIEnv* env, jobject arrayObject) {
+static bool ConvertJavaArrayToDexFiles(
+    JNIEnv* env,
+    jobject arrayObject,
+    /*out*/ std::vector<const DexFile*>& dex_files,
+    /*out*/ const OatFile*& oat_file) {
   jarray array = reinterpret_cast<jarray>(arrayObject);
 
   jsize array_size = env->GetArrayLength(array);
   if (env->ExceptionCheck() == JNI_TRUE) {
-    return std::unique_ptr<std::vector<const DexFile*>>();
+    return false;
   }
 
   // TODO: Optimize. On 32bit we can use an int array.
@@ -54,27 +57,24 @@
   jlong* long_data = env->GetLongArrayElements(reinterpret_cast<jlongArray>(array),
                                                &is_long_data_copied);
   if (env->ExceptionCheck() == JNI_TRUE) {
-    return std::unique_ptr<std::vector<const DexFile*>>();
+    return false;
   }
 
-  std::unique_ptr<std::vector<const DexFile*>> ret(new std::vector<const DexFile*>());
-  ret->reserve(array_size);
-  for (jsize i = 0; i < array_size; ++i) {
-    ret->push_back(reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(*(long_data + i))));
+  oat_file = reinterpret_cast<const OatFile*>(static_cast<uintptr_t>(long_data[kOatFileIndex]));
+  dex_files.reserve(array_size - 1);
+  for (jsize i = kDexFileIndexStart; i < array_size; ++i) {
+    dex_files.push_back(reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(long_data[i])));
   }
 
   env->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array), long_data, JNI_ABORT);
-  if (env->ExceptionCheck() == JNI_TRUE) {
-    return std::unique_ptr<std::vector<const DexFile*>>();
-  }
-
-  return ret;
+  return env->ExceptionCheck() != JNI_TRUE;
 }
 
-static jlongArray ConvertNativeToJavaArray(JNIEnv* env,
-                                           std::vector<std::unique_ptr<const DexFile>>& vec) {
-  size_t vec_size = vec.size();
-  jlongArray long_array = env->NewLongArray(static_cast<jsize>(vec_size));
+static jlongArray ConvertDexFilesToJavaArray(JNIEnv* env,
+                                             const OatFile* oat_file,
+                                             std::vector<std::unique_ptr<const DexFile>>& vec) {
+  // Add one for the oat file.
+  jlongArray long_array = env->NewLongArray(static_cast<jsize>(1u + vec.size()));
   if (env->ExceptionCheck() == JNI_TRUE) {
     return nullptr;
   }
@@ -85,10 +85,9 @@
     return nullptr;
   }
 
-  jlong* tmp = long_data;
-  for (auto& dex_file : vec) {
-    *tmp = reinterpret_cast<uintptr_t>(dex_file.get());
-    tmp++;
+  long_data[kOatFileIndex] = reinterpret_cast<uintptr_t>(oat_file);
+  for (size_t i = 0; i < vec.size(); ++i) {
+    long_data[kDexFileIndexStart + i] = reinterpret_cast<uintptr_t>(vec[i].get());
   }
 
   env->ReleaseLongArrayElements(long_array, long_data, 0);
@@ -165,13 +164,15 @@
   ClassLinker* linker = runtime->GetClassLinker();
   std::vector<std::unique_ptr<const DexFile>> dex_files;
   std::vector<std::string> error_msgs;
+  const OatFile* oat_file = nullptr;
 
   dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
                                                                outputName.c_str(),
-                                                               &error_msgs);
+                                                               /*out*/ &oat_file,
+                                                               /*out*/ &error_msgs);
 
   if (!dex_files.empty()) {
-    jlongArray array = ConvertNativeToJavaArray(env, dex_files);
+    jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
     if (array == nullptr) {
       ScopedObjectAccess soa(env);
       for (auto& dex_file : dex_files) {
@@ -197,43 +198,54 @@
 }
 
 static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) {
-  ScopedObjectAccess soa(env);
-  mirror::Object* dex_files_object = soa.Decode<mirror::Object*>(cookie);
-  if (dex_files_object == nullptr) {
-    ThrowNullPointerException("cookie == null");
+  std::vector<const DexFile*> dex_files;
+  const OatFile* oat_file;
+  if (!ConvertJavaArrayToDexFiles(env, cookie, dex_files, oat_file)) {
+    Thread::Current()->AssertPendingException();
     return JNI_FALSE;
   }
-  mirror::LongArray* dex_files = dex_files_object->AsLongArray();
-
-  // Delete dex files associated with this dalvik.system.DexFile since there should not be running
-  // code using it. dex_files is a vector due to multidex.
-  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+  Runtime* const runtime = Runtime::Current();
   bool all_deleted = true;
-  for (int32_t i = 0, count = dex_files->GetLength(); i < count; ++i) {
-    auto* dex_file = reinterpret_cast<DexFile*>(dex_files->Get(i));
-    if (dex_file == nullptr) {
-      continue;
-    }
-    // Only delete the dex file if the dex cache is not found to prevent runtime crashes if there
-    // are calls to DexFile.close while the ART DexFile is still in use.
-    if (class_linker->FindDexCache(soa.Self(), *dex_file, true) == nullptr) {
-      // Clear the element in the array so that we can call close again.
-      dex_files->Set(i, 0);
-      delete dex_file;
-    } else {
-      all_deleted = false;
+  {
+    ScopedObjectAccess soa(env);
+    mirror::Object* dex_files_object = soa.Decode<mirror::Object*>(cookie);
+    mirror::LongArray* long_dex_files = dex_files_object->AsLongArray();
+    // Delete dex files associated with this dalvik.system.DexFile since there should not be running
+    // code using it. dex_files is a vector due to multidex.
+    ClassLinker* const class_linker = runtime->GetClassLinker();
+    int32_t i = kDexFileIndexStart;  // Oat file is at index 0.
+    for (const DexFile* dex_file : dex_files) {
+      if (dex_file != nullptr) {
+        // Only delete the dex file if the dex cache is not found to prevent runtime crashes if there
+        // are calls to DexFile.close while the ART DexFile is still in use.
+        if (class_linker->FindDexCache(soa.Self(), *dex_file, true) == nullptr) {
+          // Clear the element in the array so that we can call close again.
+          long_dex_files->Set(i, 0);
+          delete dex_file;
+        } else {
+          all_deleted = false;
+        }
+      }
+      ++i;
     }
   }
 
-  // TODO: Also unmap the OatFile for this dalvik.system.DexFile.
-
+  if (all_deleted) {
+    // If all of the dex files are no longer in use we can unmap the corresponding oat file.
+    VLOG(class_linker) << "Unregistering " << oat_file;
+    runtime->GetOatFileManager().UnRegisterAndDeleteOatFile(oat_file);
+  }
   return all_deleted ? JNI_TRUE : JNI_FALSE;
 }
 
-static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
+static jclass DexFile_defineClassNative(JNIEnv* env,
+                                        jclass,
+                                        jstring javaName,
+                                        jobject javaLoader,
                                         jobject cookie) {
-  std::unique_ptr<std::vector<const DexFile*>> dex_files = ConvertJavaArrayToNative(env, cookie);
-  if (dex_files.get() == nullptr) {
+  std::vector<const DexFile*> dex_files;
+  const OatFile* oat_file;
+  if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) {
     VLOG(class_linker) << "Failed to find dex_file";
     DCHECK(env->ExceptionCheck());
     return nullptr;
@@ -246,7 +258,7 @@
   }
   const std::string descriptor(DotToDescriptor(class_name.c_str()));
   const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
-  for (auto& dex_file : *dex_files) {
+  for (auto& dex_file : dex_files) {
     const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);
     if (dex_class_def != nullptr) {
       ScopedObjectAccess soa(env);
@@ -255,8 +267,12 @@
       StackHandleScope<1> hs(soa.Self());
       Handle<mirror::ClassLoader> class_loader(
           hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
-      mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(), hash,
-                                                        class_loader, *dex_file, *dex_class_def);
+      mirror::Class* result = class_linker->DefineClass(soa.Self(),
+                                                        descriptor.c_str(),
+                                                        hash,
+                                                        class_loader,
+                                                        *dex_file,
+                                                        *dex_class_def);
       if (result != nullptr) {
         VLOG(class_linker) << "DexFile_defineClassNative returning " << result
                            << " for " << class_name.c_str();
@@ -277,8 +293,9 @@
 
 // Note: this can be an expensive call, as we sort out duplicates in MultiDex files.
 static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jobject cookie) {
-  std::unique_ptr<std::vector<const DexFile*>> dex_files = ConvertJavaArrayToNative(env, cookie);
-  if (dex_files.get() == nullptr) {
+  const OatFile* oat_file = nullptr;
+  std::vector<const DexFile*> dex_files;
+  if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) {
     DCHECK(env->ExceptionCheck());
     return nullptr;
   }
@@ -286,7 +303,7 @@
   // Push all class descriptors into a set. Use set instead of unordered_set as we want to
   // retrieve all in the end.
   std::set<const char*, CharPointerComparator> descriptors;
-  for (auto& dex_file : *dex_files) {
+  for (auto& dex_file : dex_files) {
     for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
       const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
       const char* descriptor = dex_file->GetClassDescriptor(class_def);
@@ -295,7 +312,8 @@
   }
 
   // Now create output array and copy the set into it.
-  jobjectArray result = env->NewObjectArray(descriptors.size(), WellKnownClasses::java_lang_String,
+  jobjectArray result = env->NewObjectArray(descriptors.size(),
+                                            WellKnownClasses::java_lang_String,
                                             nullptr);
   if (result != nullptr) {
     auto it = descriptors.begin();
@@ -313,9 +331,11 @@
   return result;
 }
 
-static jint GetDexOptNeeded(JNIEnv* env, const char* filename,
-    const char* pkgname, const char* instruction_set, const jboolean defer) {
-
+static jint GetDexOptNeeded(JNIEnv* env,
+                            const char* filename,
+                            const char* pkgname,
+                            const char* instruction_set,
+                            const jboolean defer) {
   if ((filename == nullptr) || !OS::FileExists(filename)) {
     LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist";
     ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
@@ -365,8 +385,12 @@
   return oat_file_assistant.GetDexOptNeeded();
 }
 
-static jint DexFile_getDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename,
-    jstring javaPkgname, jstring javaInstructionSet, jboolean defer) {
+static jint DexFile_getDexOptNeeded(JNIEnv* env,
+                                    jclass,
+                                    jstring javaFilename,
+                                    jstring javaPkgname,
+                                    jstring javaInstructionSet,
+                                    jboolean defer) {
   ScopedUtfChars filename(env, javaFilename);
   if (env->ExceptionCheck()) {
     return 0;
@@ -379,8 +403,11 @@
     return 0;
   }
 
-  return GetDexOptNeeded(env, filename.c_str(), pkgname.c_str(),
-                         instruction_set.c_str(), defer);
+  return GetDexOptNeeded(env,
+                         filename.c_str(),
+                         pkgname.c_str(),
+                         instruction_set.c_str(),
+                         defer);
 }
 
 // public API, null pkgname
diff --git a/runtime/native/dalvik_system_DexFile.h b/runtime/native/dalvik_system_DexFile.h
index 7585ab9..77d219d 100644
--- a/runtime/native/dalvik_system_DexFile.h
+++ b/runtime/native/dalvik_system_DexFile.h
@@ -18,9 +18,13 @@
 #define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_DEXFILE_H_
 
 #include <jni.h>
+#include <unistd.h>
 
 namespace art {
 
+constexpr size_t kOatFileIndex = 0;
+constexpr size_t kDexFileIndexStart = 1;
+
 class DexFile;
 
 void register_dalvik_system_DexFile(JNIEnv* env);
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 80f017d..daaa195 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -122,21 +122,29 @@
   // and for having dex caches arrays in the .bss section.
   Runtime* const runtime = Runtime::Current();
   OatFileManager* const manager = (runtime != nullptr) ? &runtime->GetOatFileManager() : nullptr;
-  if (kUseDlopen &&
-      (kIsTargetBuild ||
-          (kUseDlopenOnHost &&
-           // Manager may be null if we are running without a runtime.
-           manager != nullptr &&
-           manager->FindOpenedOatFileFromOatLocation(location) == nullptr)) &&
-      executable) {
-    // Try to use dlopen. This may fail for various reasons, outlined below. We try dlopen, as
-    // this will register the oat file with the linker and allows libunwind to find our info.
-    ret.reset(OpenDlopen(filename, location, requested_base, abs_dex_location, error_msg));
-    if (ret.get() != nullptr) {
-      return ret.release();
+  if (kUseDlopen && executable) {
+    bool success = kIsTargetBuild;
+    bool reserved_location = false;
+      // Manager may be null if we are running without a runtime.
+    if (!success && kUseDlopenOnHost && manager != nullptr) {
+      // ReserveOatFileLocation returns false if we are not the first caller to register that
+      // location.
+      reserved_location = manager->RegisterOatFileLocation(location);
+      success = reserved_location;
     }
-    if (kPrintDlOpenErrorMessage) {
-      LOG(ERROR) << "Failed to dlopen: " << *error_msg;
+    if (success) {
+      // Try to use dlopen. This may fail for various reasons, outlined below. We try dlopen, as
+      // this will register the oat file with the linker and allows libunwind to find our info.
+      ret.reset(OpenDlopen(filename, location, requested_base, abs_dex_location, error_msg));
+      if (reserved_location) {
+        manager->UnRegisterOatFileLocation(location);
+      }
+      if (ret != nullptr) {
+        return ret.release();
+      }
+      if (kPrintDlOpenErrorMessage) {
+        LOG(ERROR) << "Failed to dlopen: " << *error_msg;
+      }
     }
   }
 
@@ -217,6 +225,10 @@
       is_executable_(is_executable), dlopen_handle_(nullptr),
       secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) {
   CHECK(!location_.empty());
+  Runtime* const runtime = Runtime::Current();
+  if (runtime != nullptr && !runtime->IsAotCompiler()) {
+    runtime->GetOatFileManager().RegisterOatFileLocation(location);
+  }
 }
 
 OatFile::~OatFile() {
@@ -224,6 +236,10 @@
   if (dlopen_handle_ != nullptr) {
     dlclose(dlopen_handle_);
   }
+  Runtime* const runtime = Runtime::Current();
+  if (runtime != nullptr && !runtime->IsAotCompiler()) {
+    runtime->GetOatFileManager().UnRegisterOatFileLocation(location_);
+  }
 }
 
 bool OatFile::Dlopen(const std::string& elf_filename, uint8_t* requested_base,
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index de4e8ec..cef8702 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -961,13 +961,16 @@
     // we can verify only one oat file was loaded for the dex location.
     std::vector<std::unique_ptr<const DexFile>> dex_files;
     std::vector<std::string> error_msgs;
+    const OatFile* oat_file = nullptr;
     dex_files = Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(
         dex_location_.c_str(),
         oat_location_.c_str(),
+        &oat_file,
         &error_msgs);
     CHECK(!dex_files.empty()) << Join(error_msgs, '\n');
     CHECK(dex_files[0]->GetOatDexFile() != nullptr) << dex_files[0]->GetLocation();
     loaded_oat_file_ = dex_files[0]->GetOatDexFile()->GetOatFile();
+    CHECK_EQ(loaded_oat_file_, oat_file);
   }
 
   const OatFile* GetLoadedOatFile() const {
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 73b065f..3371a39 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -33,9 +33,10 @@
 static constexpr bool kDuplicateClassesCheck = false;
 
 const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file) {
-  ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+  WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
   DCHECK(oat_file != nullptr);
   if (kIsDebugBuild) {
+    CHECK(oat_files_.find(oat_file) == oat_files_.end());
     for (const std::unique_ptr<const OatFile>& existing : oat_files_) {
       CHECK_NE(oat_file.get(), existing.get()) << oat_file->GetLocation();
       // Check that we don't have an oat file with the same address. Copies of the same oat file
@@ -44,13 +45,29 @@
     }
   }
   have_non_pic_oat_file_ = have_non_pic_oat_file_ || !oat_file->IsPic();
-  oat_files_.push_back(std::move(oat_file));
-  return oat_files_.back().get();
+  const OatFile* ret = oat_file.get();
+  oat_files_.insert(std::move(oat_file));
+  return ret;
+}
+
+void OatFileManager::UnRegisterAndDeleteOatFile(const OatFile* oat_file) {
+  WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+  DCHECK(oat_file != nullptr);
+  std::unique_ptr<const OatFile> compare(oat_file);
+  auto it = oat_files_.find(compare);
+  CHECK(it != oat_files_.end());
+  oat_files_.erase(it);
+  compare.release();
 }
 
 const OatFile* OatFileManager::FindOpenedOatFileFromOatLocation(const std::string& oat_location)
     const {
   ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+  return FindOpenedOatFileFromOatLocationLocked(oat_location);
+}
+
+const OatFile* OatFileManager::FindOpenedOatFileFromOatLocationLocked(
+    const std::string& oat_location) const {
   for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) {
     if (oat_file->GetLocation() == oat_location) {
       return oat_file.get();
@@ -81,6 +98,9 @@
 }
 
 OatFileManager::~OatFileManager() {
+  // Explicitly clear oat_files_ since the OatFile destructor calls back into OatFileManager for
+  // UnRegisterOatFileLocation.
+  oat_files_.clear();
 }
 
 const OatFile* OatFileManager::RegisterImageOatFile(gc::space::ImageSpace* space) {
@@ -95,17 +115,9 @@
        current_class_index_(current_class_index),
        from_loaded_oat_(from_loaded_oat) {}
 
-  DexFileAndClassPair(DexFileAndClassPair&& rhs) {
-    *this = std::move(rhs);
-  }
+  DexFileAndClassPair(DexFileAndClassPair&& rhs) = default;
 
-  DexFileAndClassPair& operator=(DexFileAndClassPair&& rhs) {
-    cached_descriptor_ = rhs.cached_descriptor_;
-    dex_file_ = std::move(rhs.dex_file_);
-    current_class_index_ = rhs.current_class_index_;
-    from_loaded_oat_ = rhs.from_loaded_oat_;
-    return *this;
-  }
+  DexFileAndClassPair& operator=(DexFileAndClassPair&& rhs) = default;
 
   const char* GetCachedDescriptor() const {
     return cached_descriptor_;
@@ -127,6 +139,7 @@
 
   void Next() {
     ++current_class_index_;
+    cached_descriptor_ = nullptr;
   }
 
   size_t GetCurrentClassIndex() const {
@@ -253,6 +266,7 @@
 std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
     const char* dex_location,
     const char* oat_location,
+    const OatFile** out_oat_file,
     std::vector<std::string>* error_msgs) {
   CHECK(dex_location != nullptr);
   CHECK(error_msgs != nullptr);
@@ -311,6 +325,7 @@
     if (accept_oat_file) {
       VLOG(class_linker) << "Registering " << oat_file->GetLocation();
       source_oat_file = RegisterOatFile(std::move(oat_file));
+      *out_oat_file = source_oat_file;
     }
   }
 
@@ -344,4 +359,26 @@
   return dex_files;
 }
 
+bool OatFileManager::RegisterOatFileLocation(const std::string& oat_location) {
+  WriterMutexLock mu(Thread::Current(), *Locks::oat_file_count_lock_);
+  auto it = oat_file_count_.find(oat_location);
+  if (it != oat_file_count_.end()) {
+    ++it->second;
+    return false;
+  }
+  oat_file_count_.insert(std::pair<std::string, size_t>(oat_location, 1u));
+  return true;
+}
+
+void OatFileManager::UnRegisterOatFileLocation(const std::string& oat_location) {
+  WriterMutexLock mu(Thread::Current(), *Locks::oat_file_count_lock_);
+  auto it = oat_file_count_.find(oat_location);
+  if (it != oat_file_count_.end()) {
+    --it->second;
+    if (it->second == 0) {
+      oat_file_count_.erase(it);
+    }
+  }
+}
+
 }  // namespace art
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 3059cb5..af7efb4 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -18,7 +18,9 @@
 #define ART_RUNTIME_OAT_FILE_MANAGER_H_
 
 #include <memory>
+#include <set>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "base/macros.h"
@@ -49,10 +51,23 @@
   const OatFile* RegisterOatFile(std::unique_ptr<const OatFile> oat_file)
       REQUIRES(!Locks::oat_file_manager_lock_);
 
+  void UnRegisterAndDeleteOatFile(const OatFile* oat_file)
+      REQUIRES(!Locks::oat_file_manager_lock_);
+
   // Find the first opened oat file with the same location, returns null if there are none.
   const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) const
       REQUIRES(!Locks::oat_file_manager_lock_);
 
+  // Attempt to reserve a location, returns false if it is already reserved or already in used by
+  // an oat file.
+  bool RegisterOatFileLocation(const std::string& oat_location)
+      REQUIRES(!Locks::oat_file_count_lock_);
+
+  // Unreserve oat file location, should only be used for error cases since RegisterOatFile will
+  // remove the reserved location.
+  void UnRegisterOatFileLocation(const std::string& oat_location)
+      REQUIRES(!Locks::oat_file_count_lock_);
+
   // Returns true if we have a non pic oat file.
   bool HaveNonPicOatFile() const {
     return have_non_pic_oat_file_;
@@ -86,7 +101,8 @@
   std::vector<std::unique_ptr<const DexFile>> OpenDexFilesFromOat(
       const char* dex_location,
       const char* oat_location,
-      /*out*/std::vector<std::string>* error_msgs)
+      /*out*/ const OatFile** out_oat_file,
+      /*out*/ std::vector<std::string>* error_msgs)
       REQUIRES(!Locks::oat_file_manager_lock_, !Locks::mutator_lock_);
 
  private:
@@ -95,7 +111,11 @@
   bool HasCollisions(const OatFile* oat_file, /*out*/std::string* error_msg) const
       REQUIRES(!Locks::oat_file_manager_lock_);
 
-  std::vector<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_);
+  const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const
+      REQUIRES(Locks::oat_file_manager_lock_);
+
+  std::set<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_);
+  std::unordered_map<std::string, size_t> oat_file_count_ GUARDED_BY(Locks::oat_file_count_lock_);
   bool have_non_pic_oat_file_;
   DISALLOW_COPY_AND_ASSIGN(OatFileManager);
 };
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 7a1f0af..b623ba1 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -282,10 +282,10 @@
   delete monitor_list_;
   delete monitor_pool_;
   delete class_linker_;
-  oat_file_manager_.reset();
   delete heap_;
   delete intern_table_;
   delete java_vm_;
+  delete oat_file_manager_;
   Thread::Shutdown();
   QuasiAtomic::Shutdown();
   verifier::MethodVerifier::Shutdown();
@@ -833,7 +833,7 @@
 
   QuasiAtomic::Startup();
 
-  oat_file_manager_.reset(new OatFileManager);
+  oat_file_manager_ = new OatFileManager;
 
   Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold),
                 runtime_options.GetOrDefault(Opt::HookIsSensitiveThread));
diff --git a/runtime/runtime.h b/runtime/runtime.h
index abccb44..fd486f9 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -576,7 +576,7 @@
 
   OatFileManager& GetOatFileManager() const {
     DCHECK(oat_file_manager_ != nullptr);
-    return *oat_file_manager_.get();
+    return *oat_file_manager_;
   }
 
  private:
@@ -777,7 +777,7 @@
   std::string fingerprint_;
 
   // Oat file manager, keeps track of what oat files are open.
-  std::unique_ptr<OatFileManager> oat_file_manager_;
+  OatFileManager* oat_file_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(Runtime);
 };
diff --git a/test/141-class-unload/expected.txt b/test/141-class-unload/expected.txt
index 53d7abe..11de660 100644
--- a/test/141-class-unload/expected.txt
+++ b/test/141-class-unload/expected.txt
@@ -21,3 +21,4 @@
 JNI_OnLoad called
 class null false test
 JNI_OnUnload called
+Number of loaded unload-ex maps 0
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 3cc43ac..0640b36 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
@@ -43,11 +46,28 @@
             testStackTrace(constructor);
             // Stress test to make sure we dont leak memory.
             stressTest(constructor);
+            // Test that the oat files are unloaded.
+            testOatFilesUnloaded(getPid());
         } catch (Exception e) {
             System.out.println(e);
         }
     }
 
+    private static void testOatFilesUnloaded(int pid) throws Exception {
+        BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps"));
+        String line;
+        int count = 0;
+        Runtime.getRuntime().gc();
+        System.runFinalization();
+        while ((line = reader.readLine()) != null) {
+            if (line.contains("@141-class-unload-ex.jar")) {
+                System.out.println(line);
+                ++count;
+            }
+        }
+        System.out.println("Number of loaded unload-ex maps " + count);
+    }
+
     private static void stressTest(Constructor constructor) throws Exception {
         for (int i = 0; i <= 100; ++i) {
             setUpUnloadLoader(constructor, false);
@@ -163,4 +183,8 @@
         loadLibrary.invoke(intHolder, nativeLibraryName);
         return new WeakReference(loader);
     }
+
+    private static int getPid() throws Exception {
+      return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
+    }
 }