Allocate dex cache arrays in their class loader's linear alloc

Fixes memory leak for class unloading where the dex cache arrays
used to be in the runtime linear alloc which never got freed.

TODO: Some of the callers like the compiler just use the runtime
linear alloc. We could clean this up if we want to have class
unloading during compilation for some reason.

Added regression test.

Bug: 22720414

Change-Id: Ia50333a06a339efbdaedb5ad94b7a1ae841124ec
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 70c9dc1..1b54a51 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -65,6 +65,7 @@
 # Dex file dependencies for each gtest.
 ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Statics StaticsFromCode
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods
+ART_GTEST_dex_cache_test_DEX_DEPS := Main
 ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
 ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index e535afd..1a7dbe3 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -370,7 +370,9 @@
           nullptr, kVirtual);
     } else {
       StackHandleScope<1> hs(soa.Self());
-      auto target_dex_cache(hs.NewHandle(class_linker->RegisterDexFile(*devirt_target->dex_file)));
+      auto target_dex_cache(hs.NewHandle(class_linker->RegisterDexFile(
+          *devirt_target->dex_file,
+          class_linker->GetOrCreateAllocatorForClassLoader(class_loader.Get()))));
       called_method = class_linker->ResolveMethod(
           *devirt_target->dex_file, devirt_target->dex_method_index, target_dex_cache,
           class_loader, nullptr, kVirtual);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 74f19a1..8324bf3 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -953,7 +953,9 @@
       uint16_t exception_type_idx = exception_type.first;
       const DexFile* dex_file = exception_type.second;
       StackHandleScope<2> hs2(self);
-      Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file)));
+      Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(
+          *dex_file,
+          Runtime::Current()->GetLinearAlloc())));
       Handle<mirror::Class> klass(hs2.NewHandle(
           class_linker->ResolveType(*dex_file, exception_type_idx, dex_cache,
                                     NullHandle<mirror::ClassLoader>())));
@@ -2010,9 +2012,11 @@
     ClassLinker* class_linker = manager_->GetClassLinker();
     const DexFile& dex_file = *manager_->GetDexFile();
     StackHandleScope<2> hs(soa.Self());
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile(dex_file)));
     Handle<mirror::ClassLoader> class_loader(
         hs.NewHandle(soa.Decode<mirror::ClassLoader*>(manager_->GetClassLoader())));
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile(
+        dex_file,
+        class_linker->GetOrCreateAllocatorForClassLoader(class_loader.Get()))));
     mirror::Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader);
 
     if (klass == nullptr) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 680e2d7..17c5282 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1401,7 +1401,7 @@
       }
       ScopedObjectAccess soa(self);
       dex_caches_.push_back(soa.AddLocalReference<jobject>(
-          class_linker->RegisterDexFile(*dex_file)));
+          class_linker->RegisterDexFile(*dex_file, Runtime::Current()->GetLinearAlloc())));
     }
 
     // If we use a swap file, ensure we are above the threshold to make it necessary.
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index f5f7748..dbf5365 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1420,8 +1420,10 @@
                                          uint32_t method_access_flags) {
     if ((method_access_flags & kAccNative) == 0) {
       ScopedObjectAccess soa(Thread::Current());
+      Runtime* const runtime = Runtime::Current();
       Handle<mirror::DexCache> dex_cache(
-          hs->NewHandle(Runtime::Current()->GetClassLinker()->RegisterDexFile(*dex_file)));
+          hs->NewHandle(runtime->GetClassLinker()->RegisterDexFile(*dex_file,
+                                                                   runtime->GetLinearAlloc())));
       DCHECK(options_.class_loader_ != nullptr);
       return verifier::MethodVerifier::VerifyMethodAndDump(
           soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_,
@@ -2400,14 +2402,13 @@
   // Need to register dex files to get a working dex cache.
   ScopedObjectAccess soa(self);
   ClassLinker* class_linker = runtime->GetClassLinker();
-  Runtime::Current()->GetOatFileManager().RegisterOatFile(
-      std::unique_ptr<const OatFile>(oat_file));
+  runtime->GetOatFileManager().RegisterOatFile(std::unique_ptr<const OatFile>(oat_file));
   std::vector<const DexFile*> class_path;
   for (const OatFile::OatDexFile* odf : oat_file->GetOatDexFiles()) {
     std::string error_msg;
     const DexFile* const dex_file = OpenDexFile(odf, &error_msg);
     CHECK(dex_file != nullptr) << error_msg;
-    class_linker->RegisterDexFile(*dex_file);
+    class_linker->RegisterDexFile(*dex_file, runtime->GetLinearAlloc());
     class_path.push_back(dex_file);
   }
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 02f2e0b..27fcfb0 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1189,7 +1189,9 @@
       static_cast<mirror::Array*>(mirror::IntArray::Alloc(self, length)));
 }
 
-mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) {
+mirror::DexCache* ClassLinker::AllocDexCache(Thread* self,
+                                             const DexFile& dex_file,
+                                             LinearAlloc* linear_alloc) {
   StackHandleScope<6> hs(self);
   auto dex_cache(hs.NewHandle(down_cast<mirror::DexCache*>(
       GetClassRoot(kJavaLangDexCache)->AllocObject(self))));
@@ -1208,18 +1210,13 @@
       dex_file.NumMethodIds() != 0u || dex_file.NumFieldIds() != 0u) {
     // NOTE: We "leak" the raw_arrays because we never destroy the dex cache.
     DCHECK(image_pointer_size_ == 4u || image_pointer_size_ == 8u);
-    if (sizeof(void*) == 8u && image_pointer_size_ == 4u) {
-      // When cross-compiling for a 32-bit target on a 64-bit host, we need these arrays
-      // in the low 4GiB address space so that we can store pointers in 32-bit fields.
-      // This is conveniently provided by the linear allocator.
-      raw_arrays = reinterpret_cast<uint8_t*>(
-          Runtime::Current()->GetLinearAlloc()->Alloc(self, layout.Size()));  // Zero-initialized.
-    } else {
-      raw_arrays = reinterpret_cast<uint8_t*>(calloc(layout.Size(), 1u));  // Zero-initialized.
-      if (raw_arrays == nullptr) {
-        return nullptr;
-      }
-    }
+    // When cross-compiling for a 32-bit target on a 64-bit host, we need these arrays
+    // in the low 4GiB address space so that we can store pointers in 32-bit fields.
+    // This is conveniently provided by the linear allocator.
+    raw_arrays = reinterpret_cast<uint8_t*>(
+        (sizeof(void*) == 8u && image_pointer_size_ == 4u)
+        ? Runtime::Current()->GetLinearAlloc()->Alloc(self, layout.Size())  // Zero-initialized.
+        : linear_alloc->Alloc(self, layout.Size()));  // Zero-initialized.
   }
   GcRoot<mirror::String>* strings = (dex_file.NumStringIds() == 0u) ? nullptr :
       reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset());
@@ -1590,7 +1587,9 @@
     self->AssertPendingOOMException();
     return nullptr;
   }
-  mirror::DexCache* dex_cache = RegisterDexFile(dex_file);
+  mirror::DexCache* dex_cache = RegisterDexFile(
+      dex_file,
+      GetOrCreateAllocatorForClassLoader(class_loader.Get()));
   if (dex_cache == nullptr) {
     self->AssertPendingOOMException();
     return nullptr;
@@ -2093,6 +2092,19 @@
   return allocator;
 }
 
+LinearAlloc* ClassLinker::GetOrCreateAllocatorForClassLoader(mirror::ClassLoader* class_loader) {
+  if (class_loader == nullptr) {
+    return Runtime::Current()->GetLinearAlloc();
+  }
+  WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  LinearAlloc* allocator = class_loader->GetAllocator();
+  if (allocator == nullptr) {
+    allocator = Runtime::Current()->CreateLinearAlloc();
+    class_loader->SetAllocator(allocator);
+  }
+  return allocator;
+}
+
 void ClassLinker::LoadClassMembers(Thread* self,
                                    const DexFile& dex_file,
                                    const uint8_t* class_data,
@@ -2251,7 +2263,10 @@
 
 void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile& dex_file) {
   StackHandleScope<1> hs(self);
-  Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocDexCache(self, dex_file)));
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocDexCache(
+      self,
+      dex_file,
+      Runtime::Current()->GetLinearAlloc())));
   CHECK(dex_cache.Get() != nullptr) << "Failed to allocate dex cache for "
                                     << dex_file.GetLocation();
   AppendToBootClassPath(dex_file, dex_cache);
@@ -2287,7 +2302,7 @@
   dex_cache->SetDexFile(&dex_file);
 }
 
-mirror::DexCache* ClassLinker::RegisterDexFile(const DexFile& dex_file) {
+mirror::DexCache* ClassLinker::RegisterDexFile(const DexFile& dex_file, LinearAlloc* linear_alloc) {
   Thread* self = Thread::Current();
   {
     ReaderMutexLock mu(self, dex_lock_);
@@ -2300,7 +2315,7 @@
   // suspend all threads and another thread may need the dex_lock_ to
   // get to a suspend point.
   StackHandleScope<1> hs(self);
-  Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(AllocDexCache(self, dex_file)));
+  Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(AllocDexCache(self, dex_file, linear_alloc)));
   WriterMutexLock mu(self, dex_lock_);
   mirror::DexCache* dex_cache = FindDexCacheLocked(self, dex_file, true);
   if (dex_cache != nullptr) {
@@ -3097,6 +3112,9 @@
   std::string descriptor(GetDescriptorForProxy(klass.Get()));
   const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str());
 
+  // Needs to be before we insert the class so that the allocator field is set.
+  LinearAlloc* const allocator = GetOrCreateAllocatorForClassLoader(klass->GetClassLoader());
+
   // Insert the class before loading the fields as the field roots
   // (ArtField::declaring_class_) are only visited from the class
   // table. There can't be any suspend points between inserting the
@@ -3104,9 +3122,6 @@
   mirror::Class* existing = InsertClass(descriptor.c_str(), klass.Get(), hash);
   CHECK(existing == nullptr);
 
-  // Needs to be after we insert the class so that the allocator field is set.
-  LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader());
-
   // Instance fields are inherited, but we add a couple of static fields...
   const size_t num_fields = 2;
   LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(self, allocator, num_fields);
@@ -3945,13 +3960,13 @@
     ClassLoaderData data;
     data.weak_root = self->GetJniEnv()->vm->AddWeakGlobalRef(self, class_loader);
     data.class_table = class_table;
-    data.allocator = Runtime::Current()->CreateLinearAlloc();
-    class_loaders_.push_back(data);
     // Don't already have a class table, add it to the class loader.
     CHECK(class_loader->GetClassTable() == nullptr);
-    CHECK(class_loader->GetAllocator() == nullptr);
     class_loader->SetClassTable(data.class_table);
-    class_loader->SetAllocator(data.allocator);
+    // Should have been set when we registered the dex file.
+    data.allocator = class_loader->GetAllocator();
+    CHECK(data.allocator != nullptr);
+    class_loaders_.push_back(data);
   }
   return class_table;
 }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 93161f7..a70967d 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -319,7 +319,7 @@
       SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!dex_lock_, !Roles::uninterruptible_);
 
-  mirror::DexCache* RegisterDexFile(const DexFile& dex_file)
+  mirror::DexCache* RegisterDexFile(const DexFile& dex_file, LinearAlloc* linear_alloc)
       REQUIRES(!dex_lock_)
       SHARED_REQUIRES(Locks::mutator_lock_);
   void RegisterDexFile(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
@@ -532,6 +532,12 @@
   static LinearAlloc* GetAllocatorForClassLoader(mirror::ClassLoader* class_loader)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  // Return the linear alloc for a class loader if it is already allocated, otherwise allocate and
+  // set it. TODO: Consider using a lock other than classlinker_classes_lock_.
+  static LinearAlloc* GetOrCreateAllocatorForClassLoader(mirror::ClassLoader* class_loader)
+      SHARED_REQUIRES(Locks::mutator_lock_)
+      REQUIRES(!Locks::classlinker_classes_lock_);
+
  private:
   struct ClassLoaderData {
     jweak weak_root;  // Weak root to enable class unloading.
@@ -570,7 +576,9 @@
   mirror::Class* AllocClass(Thread* self, uint32_t class_size)
       SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!Roles::uninterruptible_);
-  mirror::DexCache* AllocDexCache(Thread* self, const DexFile& dex_file)
+  mirror::DexCache* AllocDexCache(Thread* self,
+                                  const DexFile& dex_file,
+                                  LinearAlloc* linear_alloc)
       SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!Roles::uninterruptible_);
 
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 8fb860f..48f2ca5 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -20,9 +20,8 @@
 
 #include "class_linker.h"
 #include "common_runtime_test.h"
-#include "gc/heap.h"
-#include "mirror/object_array-inl.h"
-#include "mirror/object-inl.h"
+#include "linear_alloc.h"
+#include "mirror/class_loader-inl.h"
 #include "handle_scope-inl.h"
 #include "scoped_thread_state_change.h"
 
@@ -36,7 +35,9 @@
   StackHandleScope<1> hs(soa.Self());
   ASSERT_TRUE(java_lang_dex_file_ != nullptr);
   Handle<DexCache> dex_cache(
-      hs.NewHandle(class_linker_->AllocDexCache(soa.Self(), *java_lang_dex_file_)));
+      hs.NewHandle(class_linker_->AllocDexCache(soa.Self(),
+                                                *java_lang_dex_file_,
+                                                Runtime::Current()->GetLinearAlloc())));
   ASSERT_TRUE(dex_cache.Get() != nullptr);
 
   EXPECT_EQ(java_lang_dex_file_->NumStringIds(), dex_cache->NumStrings());
@@ -45,5 +46,21 @@
   EXPECT_EQ(java_lang_dex_file_->NumFieldIds(),  dex_cache->NumResolvedFields());
 }
 
+TEST_F(DexCacheTest, LinearAlloc) {
+  ScopedObjectAccess soa(Thread::Current());
+  jobject jclass_loader(LoadDex("Main"));
+  ASSERT_TRUE(jclass_loader != nullptr);
+  Runtime* const runtime = Runtime::Current();
+  ClassLinker* const class_linker = runtime->GetClassLinker();
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LMain;", class_loader);
+  ASSERT_TRUE(klass != nullptr);
+  LinearAlloc* const linear_alloc = klass->GetClassLoader()->GetAllocator();
+  EXPECT_NE(linear_alloc, runtime->GetLinearAlloc());
+  EXPECT_TRUE(linear_alloc->Contains(klass->GetDexCache()->GetResolvedMethods()));
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index e9ce02b..9fb5d0d 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -264,10 +264,12 @@
     if (dex_class_def != nullptr) {
       ScopedObjectAccess soa(env);
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-      class_linker->RegisterDexFile(*dex_file);
       StackHandleScope<1> hs(soa.Self());
       Handle<mirror::ClassLoader> class_loader(
           hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
+      class_linker->RegisterDexFile(
+          *dex_file,
+          class_linker->GetOrCreateAllocatorForClassLoader(class_loader.Get()));
       mirror::Class* result = class_linker->DefineClass(soa.Self(),
                                                         descriptor.c_str(),
                                                         hash,
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 4f95723..4c5dc3a 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -497,7 +497,8 @@
     const DexFile* dex_file = boot_class_path[i];
     CHECK(dex_file != nullptr);
     StackHandleScope<1> hs(soa.Self());
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->RegisterDexFile(*dex_file)));
+    Handle<mirror::DexCache> dex_cache(
+        hs.NewHandle(linker->RegisterDexFile(*dex_file, runtime->GetLinearAlloc())));
 
     if (kPreloadDexCachesStrings) {
       for (size_t j = 0; j < dex_cache->NumStrings(); j++) {