Lazily allocate DexCache arrays.

We rarely need the DexCache for compiled code.
Delay the allocation in hope we never need it.

This reduces DexCache memory usage by ~25% at startup.

Test: m test-art-host-gtest
Test: test.py -r -b --host
Change-Id: I680a59c905c2b821ee954e4b32abd5d24876bd11
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index d8973d4..f76190c 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -1109,49 +1109,6 @@
   Runtime::Current()->GetClassLinker()->VisitClassLoaders(visitor);
 }
 
-void ImageWriter::ClearDexCache(ObjPtr<mirror::DexCache> dex_cache) {
-  // Clear methods.
-  mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
-  for (size_t slot_idx = 0, num = dex_cache->NumResolvedMethods(); slot_idx != num; ++slot_idx) {
-    mirror::MethodDexCachePair invalid(nullptr,
-                                       mirror::MethodDexCachePair::InvalidIndexForSlot(slot_idx));
-    mirror::DexCache::SetNativePair(resolved_methods, slot_idx, invalid);
-  }
-  // Clear fields.
-  mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields();
-  for (size_t slot_idx = 0, num = dex_cache->NumResolvedFields(); slot_idx != num; ++slot_idx) {
-    mirror::FieldDexCachePair invalid(nullptr,
-                                      mirror::FieldDexCachePair::InvalidIndexForSlot(slot_idx));
-    mirror::DexCache::SetNativePair(resolved_fields, slot_idx, invalid);
-  }
-  // Clear types.
-  mirror::TypeDexCacheType* resolved_types = dex_cache->GetResolvedTypes();
-  for (size_t slot_idx = 0, num = dex_cache->NumResolvedTypes(); slot_idx != num; ++slot_idx) {
-    mirror::TypeDexCachePair invalid(nullptr,
-                                     mirror::TypeDexCachePair::InvalidIndexForSlot(slot_idx));
-    resolved_types[slot_idx].store(invalid, std::memory_order_relaxed);
-  }
-  // Clear strings.
-  mirror::StringDexCacheType* resolved_strings = dex_cache->GetStrings();
-  for (size_t slot_idx = 0, num = dex_cache->NumStrings(); slot_idx != num; ++slot_idx) {
-    mirror::StringDexCachePair invalid(nullptr,
-                                       mirror::StringDexCachePair::InvalidIndexForSlot(slot_idx));
-    resolved_strings[slot_idx].store(invalid, std::memory_order_relaxed);
-  }
-  // Clear method types.
-  mirror::MethodTypeDexCacheType* resolved_method_types = dex_cache->GetResolvedMethodTypes();
-  size_t num_resolved_method_types = dex_cache->NumResolvedMethodTypes();
-  for (size_t slot_idx = 0; slot_idx != num_resolved_method_types; ++slot_idx) {
-    mirror::MethodTypeDexCachePair invalid(
-        nullptr, mirror::MethodTypeDexCachePair::InvalidIndexForSlot(slot_idx));
-    resolved_method_types[slot_idx].store(invalid, std::memory_order_relaxed);
-  }
-  // Clear call sites.
-  std::fill_n(dex_cache->GetResolvedCallSites(),
-              dex_cache->NumResolvedCallSites(),
-              GcRoot<mirror::CallSite>(nullptr));
-}
-
 void ImageWriter::PruneNonImageClasses() {
   Runtime* runtime = Runtime::Current();
   ClassLinker* class_linker = runtime->GetClassLinker();
@@ -1185,7 +1142,7 @@
   // Completely clear DexCaches.
   std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self);
   for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
-    ClearDexCache(dex_cache);
+    dex_cache->ResetNativeArrays();
   }
 
   // Drop the array class cache in the ClassLinker, as these are roots holding those classes live.
@@ -3146,7 +3103,8 @@
       ArtMethod* src_method = src->GetArtMethod();
       CopyAndFixupPointer(dest, mirror::Executable::ArtMethodOffset(), src_method);
     } else if (klass == GetClassRoot<mirror::DexCache>(class_roots)) {
-      down_cast<mirror::DexCache*>(copy)->ResetNativeFields();
+      down_cast<mirror::DexCache*>(copy)->SetDexFile(nullptr);
+      down_cast<mirror::DexCache*>(copy)->ResetNativeArrays();
     } else if (klass->IsClassLoaderClass()) {
       mirror::ClassLoader* copy_loader = down_cast<mirror::ClassLoader*>(copy);
       // If src is a ClassLoader, set the class table to null so that it gets recreated by the
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 1321ced..a0c2665 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -446,10 +446,6 @@
   // Remove unwanted classes from various roots.
   void PruneNonImageClasses() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Remove everything from the DexCache.
-  void ClearDexCache(ObjPtr<mirror::DexCache> dex_cache)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Find dex caches for pruning or preloading.
   std::vector<ObjPtr<mirror::DexCache>> FindDexCaches(Thread* self)
       REQUIRES_SHARED(Locks::mutator_lock_)
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 12d00ad..3e841e7 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -759,9 +759,7 @@
   }
   art::WriterMutexLock mu(driver_->self_, *art::Locks::dex_lock_);
   cache->SetLocation(location.Get());
-  cache->InitializeNativeFields(dex_file_.get(),
-                                loader.IsNull() ? driver_->runtime_->GetLinearAlloc()
-                                                : loader->GetAllocator());
+  cache->Initialize(dex_file_.get(), loader.Get());
   return cache.Get();
 }
 
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 31b81d4..2469d08 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -63,10 +63,6 @@
 template <typename MirrorType> class ObjectArray;
 class PointerArray;
 class String;
-
-template <typename T> struct NativeDexCachePair;
-using MethodDexCachePair = NativeDexCachePair<ArtMethod>;
-using MethodDexCacheType = std::atomic<MethodDexCachePair>;
 }  // namespace mirror
 
 class ArtMethod final {
diff --git a/runtime/base/locks.cc b/runtime/base/locks.cc
index 7404d0d..e530073 100644
--- a/runtime/base/locks.cc
+++ b/runtime/base/locks.cc
@@ -74,6 +74,7 @@
 Uninterruptible Roles::uninterruptible_;
 ReaderWriterMutex* Locks::jni_globals_lock_ = nullptr;
 Mutex* Locks::jni_weak_globals_lock_ = nullptr;
+Mutex* Locks::dex_cache_lock_ = nullptr;
 ReaderWriterMutex* Locks::dex_lock_ = nullptr;
 Mutex* Locks::native_debug_interface_lock_ = nullptr;
 ReaderWriterMutex* Locks::jni_id_lock_ = nullptr;
@@ -250,6 +251,10 @@
     DCHECK(dex_lock_ == nullptr);
     dex_lock_ = new ReaderWriterMutex("ClassLinker dex lock", current_lock_level);
 
+    UPDATE_CURRENT_LOCK_LEVEL(kDexCacheLock);
+    DCHECK(dex_cache_lock_ == nullptr);
+    dex_cache_lock_ = new Mutex("DexCache lock", current_lock_level);
+
     UPDATE_CURRENT_LOCK_LEVEL(kOatFileManagerLock);
     DCHECK(oat_file_manager_lock_ == nullptr);
     oat_file_manager_lock_ = new ReaderWriterMutex("OatFile manager lock", current_lock_level);
diff --git a/runtime/base/locks.h b/runtime/base/locks.h
index 7008539..25e503c 100644
--- a/runtime/base/locks.h
+++ b/runtime/base/locks.h
@@ -97,6 +97,7 @@
   kTracingStreamingLock,
   kClassLoaderClassesLock,
   kDefaultMutexLevel,
+  kDexCacheLock,
   kDexLock,
   kMarkSweepLargeObjectLock,
   kJdwpObjectRegistryLock,
@@ -290,6 +291,8 @@
 
   static ReaderWriterMutex* dex_lock_ ACQUIRED_AFTER(modify_ldt_lock_);
 
+  static Mutex* dex_cache_lock_ ACQUIRED_AFTER(dex_lock_);
+
   // Guards opened oat files in OatFileManager.
   static ReaderWriterMutex* oat_file_manager_lock_ ACQUIRED_AFTER(dex_lock_);
 
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 69f5a77..f45ccb5 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -70,10 +70,7 @@
                                                          ArtField* referrer) {
   Thread::PoisonObjectPointersIfDebug();
   DCHECK(!Thread::Current()->IsExceptionPending());
-  // We do not need the read barrier for getting the DexCache for the initial resolved type
-  // lookup as both from-space and to-space copies point to the same native resolved types array.
-  ObjPtr<mirror::String> resolved =
-      referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedString(string_idx);
+  ObjPtr<mirror::String> resolved = referrer->GetDexCache()->GetResolvedString(string_idx);
   if (resolved == nullptr) {
     resolved = DoResolveString(string_idx, referrer->GetDexCache());
   }
@@ -84,10 +81,7 @@
                                                          ArtMethod* referrer) {
   Thread::PoisonObjectPointersIfDebug();
   DCHECK(!Thread::Current()->IsExceptionPending());
-  // We do not need the read barrier for getting the DexCache for the initial resolved type
-  // lookup as both from-space and to-space copies point to the same native resolved types array.
-  ObjPtr<mirror::String> resolved =
-      referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedString(string_idx);
+  ObjPtr<mirror::String> resolved = referrer->GetDexCache()->GetResolvedString(string_idx);
   if (resolved == nullptr) {
     resolved = DoResolveString(string_idx, referrer->GetDexCache());
   }
@@ -122,10 +116,8 @@
     Thread::Current()->PoisonObjectPointers();
   }
   DCHECK(!Thread::Current()->IsExceptionPending());
-  // We do not need the read barrier for getting the DexCache for the initial resolved type
-  // lookup as both from-space and to-space copies point to the same native resolved types array.
   ObjPtr<mirror::Class> resolved_type =
-      referrer->GetDexCache<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetResolvedType(type_idx);
+      referrer->GetDexCache<kDefaultVerifyFlags>()->GetResolvedType(type_idx);
   if (resolved_type == nullptr) {
     resolved_type = DoResolveType(type_idx, referrer);
   }
@@ -136,10 +128,7 @@
                                                       ArtField* referrer) {
   Thread::PoisonObjectPointersIfDebug();
   DCHECK(!Thread::Current()->IsExceptionPending());
-  // We do not need the read barrier for getting the DexCache for the initial resolved type
-  // lookup as both from-space and to-space copies point to the same native resolved types array.
-  ObjPtr<mirror::Class> resolved_type =
-      referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx);
+  ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx);
   if (UNLIKELY(resolved_type == nullptr)) {
     resolved_type = DoResolveType(type_idx, referrer);
   }
@@ -150,10 +139,7 @@
                                                       ArtMethod* referrer) {
   Thread::PoisonObjectPointersIfDebug();
   DCHECK(!Thread::Current()->IsExceptionPending());
-  // We do not need the read barrier for getting the DexCache for the initial resolved type
-  // lookup as both from-space and to-space copies point to the same native resolved types array.
-  ObjPtr<mirror::Class> resolved_type =
-      referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx);
+  ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx);
   if (UNLIKELY(resolved_type == nullptr)) {
     resolved_type = DoResolveType(type_idx, referrer);
   }
@@ -174,10 +160,8 @@
 
 inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(dex::TypeIndex type_idx,
                                                              ObjPtr<mirror::Class> referrer) {
-  // We do not need the read barrier for getting the DexCache for the initial resolved type
-  // lookup as both from-space and to-space copies point to the same native resolved types array.
   ObjPtr<mirror::Class> type =
-      referrer->GetDexCache<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetResolvedType(type_idx);
+      referrer->GetDexCache<kDefaultVerifyFlags>()->GetResolvedType(type_idx);
   if (type == nullptr) {
     type = DoLookupResolvedType(type_idx, referrer);
   }
@@ -186,10 +170,7 @@
 
 inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(dex::TypeIndex type_idx,
                                                              ArtField* referrer) {
-  // We do not need the read barrier for getting the DexCache for the initial resolved type
-  // lookup as both from-space and to-space copies point to the same native resolved types array.
-  ObjPtr<mirror::Class> type =
-      referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx);
+  ObjPtr<mirror::Class> type = referrer->GetDexCache()->GetResolvedType(type_idx);
   if (type == nullptr) {
     type = DoLookupResolvedType(type_idx, referrer->GetDeclaringClass());
   }
@@ -198,10 +179,7 @@
 
 inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(dex::TypeIndex type_idx,
                                                              ArtMethod* referrer) {
-  // We do not need the read barrier for getting the DexCache for the initial resolved type
-  // lookup as both from-space and to-space copies point to the same native resolved types array.
-  ObjPtr<mirror::Class> type =
-      referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx);
+  ObjPtr<mirror::Class> type = referrer->GetDexCache()->GetResolvedType(type_idx);
   if (type == nullptr) {
     type = DoLookupResolvedType(type_idx, referrer->GetDeclaringClass());
   }
@@ -307,10 +285,7 @@
   // lookup in the context of the original method from where it steals the code.
   // However, we delay the GetInterfaceMethodIfProxy() until needed.
   DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
-  // We do not need the read barrier for getting the DexCache for the initial resolved method
-  // lookup as both from-space and to-space copies point to the same native resolved methods array.
-  ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod(
-      method_idx);
+  ArtMethod* resolved_method = referrer->GetDexCache()->GetResolvedMethod(method_idx);
   if (resolved_method == nullptr) {
     return nullptr;
   }
@@ -350,10 +325,7 @@
   // However, we delay the GetInterfaceMethodIfProxy() until needed.
   DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
   Thread::PoisonObjectPointersIfDebug();
-  // We do not need the read barrier for getting the DexCache for the initial resolved method
-  // lookup as both from-space and to-space copies point to the same native resolved methods array.
-  ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod(
-      method_idx);
+  ArtMethod* resolved_method = referrer->GetDexCache()->GetResolvedMethod(method_idx);
   DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod());
   if (UNLIKELY(resolved_method == nullptr)) {
     referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
@@ -418,10 +390,7 @@
 inline ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx,
                                                   ArtMethod* referrer,
                                                   bool is_static) {
-  // We do not need the read barrier for getting the DexCache for the initial resolved field
-  // lookup as both from-space and to-space copies point to the same native resolved fields array.
-  ArtField* field = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedField(
-      field_idx);
+  ArtField* field = referrer->GetDexCache()->GetResolvedField(field_idx);
   if (field == nullptr) {
     ObjPtr<mirror::ClassLoader> class_loader = referrer->GetDeclaringClass()->GetClassLoader();
     field = LookupResolvedField(field_idx, referrer->GetDexCache(), class_loader, is_static);
@@ -433,10 +402,7 @@
                                            ArtMethod* referrer,
                                            bool is_static) {
   Thread::PoisonObjectPointersIfDebug();
-  // We do not need the read barrier for getting the DexCache for the initial resolved field
-  // lookup as both from-space and to-space copies point to the same native resolved fields array.
-  ArtField* resolved_field = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedField(
-      field_idx);
+  ArtField* resolved_field = referrer->GetDexCache()->GetResolvedField(field_idx);
   if (UNLIKELY(resolved_field == nullptr)) {
     StackHandleScope<2> hs(Thread::Current());
     ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b98708e..0fe8caa 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1937,13 +1937,10 @@
       return false;
     }
 
-    LinearAlloc* linear_alloc = GetOrCreateAllocatorForClassLoader(class_loader.Get());
-    DCHECK(linear_alloc != nullptr);
-    DCHECK_EQ(linear_alloc == Runtime::Current()->GetLinearAlloc(), !app_image);
     {
-      // Native fields are all null.  Initialize them and allocate native memory.
+      // Native fields are all null.  Initialize them.
       WriterMutexLock mu(self, *Locks::dex_lock_);
-      dex_cache->InitializeNativeFields(dex_file.get(), linear_alloc);
+      dex_cache->Initialize(dex_file.get(), class_loader.Get());
     }
     if (!app_image) {
       // Register dex files, keep track of existing ones that are conflicts.
@@ -2404,13 +2401,14 @@
   return dex_cache.Get();
 }
 
-ObjPtr<mirror::DexCache> ClassLinker::AllocAndInitializeDexCache(Thread* self,
-                                                                 const DexFile& dex_file,
-                                                                 LinearAlloc* linear_alloc) {
+ObjPtr<mirror::DexCache> ClassLinker::AllocAndInitializeDexCache(
+    Thread* self, const DexFile& dex_file, ObjPtr<mirror::ClassLoader> class_loader) {
+  StackHandleScope<1> hs(self);
+  Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader));
   ObjPtr<mirror::DexCache> dex_cache = AllocDexCache(self, dex_file);
   if (dex_cache != nullptr) {
     WriterMutexLock mu(self, *Locks::dex_lock_);
-    dex_cache->InitializeNativeFields(&dex_file, linear_alloc);
+    dex_cache->Initialize(&dex_file, h_class_loader.Get());
   }
   return dex_cache;
 }
@@ -3845,10 +3843,8 @@
 }
 
 void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile* dex_file) {
-  ObjPtr<mirror::DexCache> dex_cache = AllocAndInitializeDexCache(
-      self,
-      *dex_file,
-      Runtime::Current()->GetLinearAlloc());
+  ObjPtr<mirror::DexCache> dex_cache =
+      AllocAndInitializeDexCache(self, *dex_file, /* class_loader= */ nullptr);
   CHECK(dex_cache != nullptr) << "Failed to allocate dex cache for " << dex_file->GetLocation();
   AppendToBootClassPath(dex_file, dex_cache);
 }
@@ -4038,10 +4034,10 @@
     const DexCacheData* old_data = FindDexCacheDataLocked(dex_file);
     old_dex_cache = DecodeDexCacheLocked(self, old_data);
     if (old_dex_cache == nullptr && h_dex_cache != nullptr) {
-      // Do InitializeNativeFields while holding dex lock to make sure two threads don't call it
+      // Do Initialize while holding dex lock to make sure two threads don't call it
       // at the same time with the same dex cache. Since the .bss is shared this can cause failing
       // DCHECK that the arrays are null.
-      h_dex_cache->InitializeNativeFields(&dex_file, linear_alloc);
+      h_dex_cache->Initialize(&dex_file, h_class_loader.Get());
       RegisterDexFileLocked(dex_file, h_dex_cache.Get(), h_class_loader.Get());
     }
     if (old_dex_cache != nullptr) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 95dc6cf..d1212a3 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -93,9 +93,6 @@
 class MethodType;
 template<class T> class ObjectArray;
 class StackTraceElement;
-template <typename T> struct NativeDexCachePair;
-using MethodDexCachePair = NativeDexCachePair<ArtMethod>;
-using MethodDexCacheType = std::atomic<MethodDexCachePair>;
 }  // namespace mirror
 
 class ClassVisitor {
@@ -952,7 +949,7 @@
   // Used for tests and AppendToBootClassPath.
   ObjPtr<mirror::DexCache> AllocAndInitializeDexCache(Thread* self,
                                                       const DexFile& dex_file,
-                                                      LinearAlloc* linear_alloc)
+                                                      ObjPtr<mirror::ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_)
       REQUIRES(!Roles::uninterruptible_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index c561c4d..53908d8 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1544,8 +1544,9 @@
                                                         nullptr,
                                                         nullptr));
   // Make a copy of the dex cache with changed name.
-  LinearAlloc* alloc = Runtime::Current()->GetLinearAlloc();
-  dex_cache.Assign(class_linker->AllocAndInitializeDexCache(Thread::Current(), *dex_file, alloc));
+  dex_cache.Assign(class_linker->AllocAndInitializeDexCache(Thread::Current(),
+                                                            *dex_file,
+                                                            /* class_loader= */ nullptr));
   DCHECK_EQ(dex_cache->GetLocation()->CompareTo(location.Get()), 0);
   {
     WriterMutexLock mu(soa.Self(), *Locks::dex_lock_);
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 5927c92..c8506a0 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -763,11 +763,8 @@
   // This effectively inlines the fast path from ArtMethod::GetDexCache.
   ArtMethod* referrer = shadow_frame->GetMethod();
   if (LIKELY(!referrer->IsObsolete() && !do_access_checks)) {
-    // Avoid read barriers, since we need only the pointer to the native (non-movable)
-    // DexCache field array which we can get even through from-space objects.
-    ObjPtr<mirror::Class> klass = referrer->GetDeclaringClass<kWithoutReadBarrier>();
-    ObjPtr<mirror::DexCache> dex_cache =
-        klass->GetDexCache<kDefaultVerifyFlags, kWithoutReadBarrier>();
+    ObjPtr<mirror::Class> klass = referrer->GetDeclaringClass();
+    ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache<kDefaultVerifyFlags>();
 
     // Try to find the desired field in DexCache.
     uint32_t field_idx = kIsStatic ? inst->VRegB_21c() : inst->VRegC_22c();
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 1ca4202..730bc47 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -28,6 +28,7 @@
 #include "class_linker.h"
 #include "dex/dex_file.h"
 #include "gc_root-inl.h"
+#include "linear_alloc.h"
 #include "mirror/call_site.h"
 #include "mirror/class.h"
 #include "mirror/method_type.h"
@@ -41,6 +42,38 @@
 namespace art {
 namespace mirror {
 
+template<typename DexCachePair>
+static void InitializeArray(std::atomic<DexCachePair>* array) {
+  DexCachePair::Initialize(array);
+}
+
+template<typename T>
+static void InitializeArray(GcRoot<T>*) {
+  // No special initialization is needed.
+}
+
+template<typename T, size_t kMaxCacheSize>
+T* DexCache::AllocArray(MemberOffset obj_offset, MemberOffset num_offset, size_t num) {
+  num = std::min<size_t>(num, kMaxCacheSize);
+  if (num == 0) {
+    return nullptr;
+  }
+  Thread* self = Thread::Current();
+  ClassLinker* linker = Runtime::Current()->GetClassLinker();
+  LinearAlloc* alloc = linker->GetOrCreateAllocatorForClassLoader(GetClassLoader());
+  MutexLock mu(self, *Locks::dex_cache_lock_);  // Avoid allocation by multiple threads.
+  T* array = GetFieldPtr64<T*>(obj_offset);
+  if (array != nullptr) {
+    DCHECK(alloc->Contains(array));
+    return array;  // Other thread just allocated the array.
+  }
+  array = reinterpret_cast<T*>(alloc->AllocAlign16(self, RoundUp(num * sizeof(T), 16)));
+  InitializeArray(array);  // Ensure other threads see the array initialized.
+  SetField32Volatile<false, false>(num_offset, num);
+  SetField64Volatile<false, false>(obj_offset, reinterpret_cast<uint64_t>(array));
+  return array;
+}
+
 template <typename T>
 inline DexCachePair<T>::DexCachePair(ObjPtr<T> object, uint32_t index)
     : object(object), index(index) {}
@@ -83,27 +116,22 @@
 }
 
 inline String* DexCache::GetResolvedString(dex::StringIndex string_idx) {
-  const uint32_t num_preresolved_strings = NumPreResolvedStrings();
-  if (num_preresolved_strings != 0u) {
-    GcRoot<mirror::String>* preresolved_strings = GetPreResolvedStrings();
-    // num_preresolved_strings can become 0 and preresolved_strings can become null in any order
-    // when ClearPreResolvedStrings is called.
-    if (preresolved_strings != nullptr) {
-      DCHECK_LT(string_idx.index_, num_preresolved_strings);
-      DCHECK_EQ(num_preresolved_strings, GetDexFile()->NumStringIds());
-      mirror::String* string = preresolved_strings[string_idx.index_].Read();
-      if (LIKELY(string != nullptr)) {
-        return string;
-      }
-    }
+  StringDexCacheType* strings = GetStrings();
+  if (UNLIKELY(strings == nullptr)) {
+    return nullptr;
   }
-  return GetStrings()[StringSlotIndex(string_idx)].load(
+  return strings[StringSlotIndex(string_idx)].load(
       std::memory_order_relaxed).GetObjectForIndex(string_idx.index_);
 }
 
 inline void DexCache::SetResolvedString(dex::StringIndex string_idx, ObjPtr<String> resolved) {
   DCHECK(resolved != nullptr);
-  GetStrings()[StringSlotIndex(string_idx)].store(
+  StringDexCacheType* strings = GetStrings();
+  if (UNLIKELY(strings == nullptr)) {
+    strings = AllocArray<StringDexCacheType, kDexCacheStringCacheSize>(
+        StringsOffset(), NumStringsOffset(), GetDexFile()->NumStringIds());
+  }
+  strings[StringSlotIndex(string_idx)].store(
       StringDexCachePair(resolved, string_idx.index_), std::memory_order_relaxed);
   Runtime* const runtime = Runtime::Current();
   if (UNLIKELY(runtime->IsActiveTransaction())) {
@@ -114,32 +142,14 @@
   WriteBarrier::ForEveryFieldWrite(this);
 }
 
-inline void DexCache::SetPreResolvedString(dex::StringIndex string_idx, ObjPtr<String> resolved) {
-  DCHECK(resolved != nullptr);
-  DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds());
-  GetPreResolvedStrings()[string_idx.index_] = GcRoot<mirror::String>(resolved);
-  Runtime* const runtime = Runtime::Current();
-  CHECK(runtime->IsAotCompiler());
-  CHECK(!runtime->IsActiveTransaction());
-  // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
-  WriteBarrier::ForEveryFieldWrite(this);
-}
-
-inline void DexCache::ClearPreResolvedStrings() {
-  SetFieldPtr64</*kTransactionActive=*/false,
-                /*kCheckTransaction=*/false,
-                kVerifyNone,
-                GcRoot<mirror::String>*>(PreResolvedStringsOffset(), nullptr);
-  SetField32</*kTransactionActive=*/false,
-             /*bool kCheckTransaction=*/false,
-             kVerifyNone,
-             /*kIsVolatile=*/false>(NumPreResolvedStringsOffset(), 0);
-}
-
 inline void DexCache::ClearString(dex::StringIndex string_idx) {
   DCHECK(Runtime::Current()->IsAotCompiler());
   uint32_t slot_idx = StringSlotIndex(string_idx);
-  StringDexCacheType* slot = &GetStrings()[slot_idx];
+  StringDexCacheType* strings = GetStrings();
+  if (UNLIKELY(strings == nullptr)) {
+    return;
+  }
+  StringDexCacheType* slot = &strings[slot_idx];
   // This is racy but should only be called from the transactional interpreter.
   if (slot->load(std::memory_order_relaxed).index == string_idx.index_) {
     StringDexCachePair cleared(nullptr, StringDexCachePair::InvalidIndexForSlot(slot_idx));
@@ -157,18 +167,27 @@
 inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) {
   // It is theorized that a load acquire is not required since obtaining the resolved class will
   // always have an address dependency or a lock.
-  return GetResolvedTypes()[TypeSlotIndex(type_idx)].load(
+  TypeDexCacheType* resolved_types = GetResolvedTypes();
+  if (UNLIKELY(resolved_types == nullptr)) {
+    return nullptr;
+  }
+  return resolved_types[TypeSlotIndex(type_idx)].load(
       std::memory_order_relaxed).GetObjectForIndex(type_idx.index_);
 }
 
 inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) {
   DCHECK(resolved != nullptr);
   DCHECK(resolved->IsResolved()) << resolved->GetStatus();
+  TypeDexCacheType* resolved_types = GetResolvedTypes();
+  if (UNLIKELY(resolved_types == nullptr)) {
+    resolved_types = AllocArray<TypeDexCacheType, kDexCacheTypeCacheSize>(
+        ResolvedTypesOffset(), NumResolvedTypesOffset(), GetDexFile()->NumTypeIds());
+  }
   // TODO default transaction support.
   // Use a release store for SetResolvedType. This is done to prevent other threads from seeing a
   // class but not necessarily seeing the loaded members like the static fields array.
   // See b/32075261.
-  GetResolvedTypes()[TypeSlotIndex(type_idx)].store(
+  resolved_types[TypeSlotIndex(type_idx)].store(
       TypeDexCachePair(resolved, type_idx.index_), std::memory_order_release);
   // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
   WriteBarrier::ForEveryFieldWrite(this);
@@ -176,8 +195,12 @@
 
 inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) {
   DCHECK(Runtime::Current()->IsAotCompiler());
+  TypeDexCacheType* resolved_types = GetResolvedTypes();
+  if (UNLIKELY(resolved_types == nullptr)) {
+    return;
+  }
   uint32_t slot_idx = TypeSlotIndex(type_idx);
-  TypeDexCacheType* slot = &GetResolvedTypes()[slot_idx];
+  TypeDexCacheType* slot = &resolved_types[slot_idx];
   // This is racy but should only be called from the single-threaded ImageWriter and tests.
   if (slot->load(std::memory_order_relaxed).index == type_idx.index_) {
     TypeDexCachePair cleared(nullptr, TypeDexCachePair::InvalidIndexForSlot(slot_idx));
@@ -194,13 +217,22 @@
 }
 
 inline MethodType* DexCache::GetResolvedMethodType(dex::ProtoIndex proto_idx) {
-  return GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].load(
+  MethodTypeDexCacheType* methods = GetResolvedMethodTypes();
+  if (UNLIKELY(methods == nullptr)) {
+    return nullptr;
+  }
+  return methods[MethodTypeSlotIndex(proto_idx)].load(
       std::memory_order_relaxed).GetObjectForIndex(proto_idx.index_);
 }
 
 inline void DexCache::SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) {
   DCHECK(resolved != nullptr);
-  GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].store(
+  MethodTypeDexCacheType* methods = GetResolvedMethodTypes();
+  if (UNLIKELY(methods == nullptr)) {
+    methods = AllocArray<MethodTypeDexCacheType, kDexCacheMethodTypeCacheSize>(
+        ResolvedMethodTypesOffset(), NumResolvedMethodTypesOffset(), GetDexFile()->NumProtoIds());
+  }
+  methods[MethodTypeSlotIndex(proto_idx)].store(
       MethodTypeDexCachePair(resolved, proto_idx.index_), std::memory_order_relaxed);
   // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
   WriteBarrier::ForEveryFieldWrite(this);
@@ -209,7 +241,11 @@
 inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) {
   DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
   DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
-  GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx];
+  GcRoot<CallSite>* call_sites = GetResolvedCallSites();
+  if (UNLIKELY(call_sites == nullptr)) {
+    return nullptr;
+  }
+  GcRoot<mirror::CallSite>& target = call_sites[call_site_idx];
   Atomic<GcRoot<mirror::CallSite>>& ref =
       reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target);
   return ref.load(std::memory_order_seq_cst).Read();
@@ -222,7 +258,12 @@
 
   GcRoot<mirror::CallSite> null_call_site(nullptr);
   GcRoot<mirror::CallSite> candidate(call_site);
-  GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx];
+  GcRoot<CallSite>* call_sites = GetResolvedCallSites();
+  if (UNLIKELY(call_sites == nullptr)) {
+    call_sites = AllocArray<GcRoot<CallSite>, std::numeric_limits<size_t>::max()>(
+        ResolvedCallSitesOffset(), NumResolvedCallSitesOffset(), GetDexFile()->NumCallSiteIds());
+  }
+  GcRoot<mirror::CallSite>& target = call_sites[call_site_idx];
 
   // The first assignment for a given call site wins.
   Atomic<GcRoot<mirror::CallSite>>& ref =
@@ -244,14 +285,23 @@
 }
 
 inline ArtField* DexCache::GetResolvedField(uint32_t field_idx) {
-  auto pair = GetNativePair(GetResolvedFields(), FieldSlotIndex(field_idx));
+  FieldDexCacheType* fields = GetResolvedFields();
+  if (UNLIKELY(fields == nullptr)) {
+    return nullptr;
+  }
+  auto pair = GetNativePair(fields, FieldSlotIndex(field_idx));
   return pair.GetObjectForIndex(field_idx);
 }
 
 inline void DexCache::SetResolvedField(uint32_t field_idx, ArtField* field) {
   DCHECK(field != nullptr);
   FieldDexCachePair pair(field, field_idx);
-  SetNativePair(GetResolvedFields(), FieldSlotIndex(field_idx), pair);
+  FieldDexCacheType* fields = GetResolvedFields();
+  if (UNLIKELY(fields == nullptr)) {
+    fields = AllocArray<FieldDexCacheType, kDexCacheFieldCacheSize>(
+        ResolvedFieldsOffset(), NumResolvedFieldsOffset(), GetDexFile()->NumFieldIds());
+  }
+  SetNativePair(fields, FieldSlotIndex(field_idx), pair);
 }
 
 inline uint32_t DexCache::MethodSlotIndex(uint32_t method_idx) {
@@ -262,14 +312,23 @@
 }
 
 inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx) {
-  auto pair = GetNativePair(GetResolvedMethods(), MethodSlotIndex(method_idx));
+  MethodDexCacheType* methods = GetResolvedMethods();
+  if (UNLIKELY(methods == nullptr)) {
+    return nullptr;
+  }
+  auto pair = GetNativePair(methods, MethodSlotIndex(method_idx));
   return pair.GetObjectForIndex(method_idx);
 }
 
 inline void DexCache::SetResolvedMethod(uint32_t method_idx, ArtMethod* method) {
   DCHECK(method != nullptr);
   MethodDexCachePair pair(method, method_idx);
-  SetNativePair(GetResolvedMethods(), MethodSlotIndex(method_idx), pair);
+  MethodDexCacheType* methods = GetResolvedMethods();
+  if (UNLIKELY(methods == nullptr)) {
+    methods = AllocArray<MethodDexCacheType, kDexCacheMethodCacheSize>(
+        ResolvedMethodsOffset(), NumResolvedMethodsOffset(), GetDexFile()->NumMethodIds());
+  }
+  SetNativePair(methods, MethodSlotIndex(method_idx), pair);
 }
 
 template <typename T>
@@ -310,7 +369,7 @@
                                size_t num_pairs,
                                const Visitor& visitor)
     REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
-  for (size_t i = 0; i < num_pairs; ++i) {
+  for (size_t i = 0; pairs != nullptr && i < num_pairs; ++i) {
     DexCachePair<T> source = pairs[i].load(std::memory_order_relaxed);
     // NOTE: We need the "template" keyword here to avoid a compilation
     // failure. GcRoot<T> is a template argument-dependent type and we need to
@@ -345,65 +404,9 @@
 
     GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites<kVerifyFlags>();
     size_t num_call_sites = NumResolvedCallSites<kVerifyFlags>();
-    for (size_t i = 0; i != num_call_sites; ++i) {
+    for (size_t i = 0; resolved_call_sites != nullptr && i != num_call_sites; ++i) {
       visitor.VisitRootIfNonNull(resolved_call_sites[i].AddressWithoutBarrier());
     }
-
-    GcRoot<mirror::String>* const preresolved_strings = GetPreResolvedStrings();
-    if (preresolved_strings != nullptr) {
-      const size_t num_preresolved_strings = NumPreResolvedStrings();
-      for (size_t i = 0; i != num_preresolved_strings; ++i) {
-        visitor.VisitRootIfNonNull(preresolved_strings[i].AddressWithoutBarrier());
-      }
-    }
-  }
-}
-
-template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupStrings(StringDexCacheType* dest, const Visitor& visitor) {
-  StringDexCacheType* src = GetStrings();
-  for (size_t i = 0, count = NumStrings(); i < count; ++i) {
-    StringDexCachePair source = src[i].load(std::memory_order_relaxed);
-    String* ptr = source.object.Read<kReadBarrierOption>();
-    String* new_source = visitor(ptr);
-    source.object = GcRoot<String>(new_source);
-    dest[i].store(source, std::memory_order_relaxed);
-  }
-}
-
-template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupResolvedTypes(TypeDexCacheType* dest, const Visitor& visitor) {
-  TypeDexCacheType* src = GetResolvedTypes();
-  for (size_t i = 0, count = NumResolvedTypes(); i < count; ++i) {
-    TypeDexCachePair source = src[i].load(std::memory_order_relaxed);
-    Class* ptr = source.object.Read<kReadBarrierOption>();
-    Class* new_source = visitor(ptr);
-    source.object = GcRoot<Class>(new_source);
-    dest[i].store(source, std::memory_order_relaxed);
-  }
-}
-
-template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupResolvedMethodTypes(MethodTypeDexCacheType* dest,
-                                               const Visitor& visitor) {
-  MethodTypeDexCacheType* src = GetResolvedMethodTypes();
-  for (size_t i = 0, count = NumResolvedMethodTypes(); i < count; ++i) {
-    MethodTypeDexCachePair source = src[i].load(std::memory_order_relaxed);
-    MethodType* ptr = source.object.Read<kReadBarrierOption>();
-    MethodType* new_source = visitor(ptr);
-    source.object = GcRoot<MethodType>(new_source);
-    dest[i].store(source, std::memory_order_relaxed);
-  }
-}
-
-template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest,
-                                             const Visitor& visitor) {
-  GcRoot<mirror::CallSite>* src = GetResolvedCallSites();
-  for (size_t i = 0, count = NumResolvedCallSites(); i < count; ++i) {
-    mirror::CallSite* source = src[i].Read<kReadBarrierOption>();
-    mirror::CallSite* new_source = visitor(source);
-    dest[i] = GcRoot<mirror::CallSite>(new_source);
   }
 }
 
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 1b9558e..fda827d 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -35,15 +35,7 @@
 namespace art {
 namespace mirror {
 
-template<typename T>
-static T* AllocArray(Thread* self, LinearAlloc* alloc, size_t num) {
-  if (num == 0) {
-    return nullptr;
-  }
-  return reinterpret_cast<T*>(alloc->AllocAlign16(self, RoundUp(num * sizeof(T), 16)));
-}
-
-void DexCache::InitializeNativeFields(const DexFile* dex_file, LinearAlloc* linear_alloc) {
+void DexCache::Initialize(const DexFile* dex_file, ObjPtr<ClassLoader> class_loader) {
   DCHECK(GetDexFile() == nullptr);
   DCHECK(GetStrings() == nullptr);
   DCHECK(GetResolvedTypes() == nullptr);
@@ -53,94 +45,9 @@
   DCHECK(GetResolvedCallSites() == nullptr);
 
   ScopedAssertNoThreadSuspension sants(__FUNCTION__);
-  Thread* self = Thread::Current();
 
-  size_t num_strings = std::min<size_t>(kDexCacheStringCacheSize, dex_file->NumStringIds());
-  size_t num_types = std::min<size_t>(kDexCacheTypeCacheSize, dex_file->NumTypeIds());
-  size_t num_fields = std::min<size_t>(kDexCacheFieldCacheSize, dex_file->NumFieldIds());
-  size_t num_methods = std::min<size_t>(kDexCacheMethodCacheSize, dex_file->NumMethodIds());
-  size_t num_method_types = std::min<size_t>(kDexCacheMethodTypeCacheSize, dex_file->NumProtoIds());
-  size_t num_call_sites = dex_file->NumCallSiteIds();  // Full size.
-
-  static_assert(ArenaAllocator::kAlignment == 8, "Expecting arena alignment of 8.");
-  StringDexCacheType* strings =
-      AllocArray<StringDexCacheType>(self, linear_alloc, num_strings);
-  TypeDexCacheType* types =
-      AllocArray<TypeDexCacheType>(self, linear_alloc, num_types);
-  MethodDexCacheType* methods =
-      AllocArray<MethodDexCacheType>(self, linear_alloc, num_methods);
-  FieldDexCacheType* fields =
-      AllocArray<FieldDexCacheType>(self, linear_alloc, num_fields);
-  MethodTypeDexCacheType* method_types =
-      AllocArray<MethodTypeDexCacheType>(self, linear_alloc, num_method_types);
-  GcRoot<mirror::CallSite>* call_sites =
-      AllocArray<GcRoot<CallSite>>(self, linear_alloc, num_call_sites);
-
-  DCHECK_ALIGNED(types, alignof(StringDexCacheType)) <<
-                 "Expected StringsOffset() to align to StringDexCacheType.";
-  DCHECK_ALIGNED(strings, alignof(StringDexCacheType)) <<
-                 "Expected strings to align to StringDexCacheType.";
-  static_assert(alignof(StringDexCacheType) == 8u,
-                "Expected StringDexCacheType to have align of 8.");
-  if (kIsDebugBuild) {
-    // Consistency check to make sure all the dex cache arrays are empty. b/28992179
-    for (size_t i = 0; i < num_strings; ++i) {
-      CHECK_EQ(strings[i].load(std::memory_order_relaxed).index, 0u);
-      CHECK(strings[i].load(std::memory_order_relaxed).object.IsNull());
-    }
-    for (size_t i = 0; i < num_types; ++i) {
-      CHECK_EQ(types[i].load(std::memory_order_relaxed).index, 0u);
-      CHECK(types[i].load(std::memory_order_relaxed).object.IsNull());
-    }
-    for (size_t i = 0; i < num_methods; ++i) {
-      CHECK_EQ(GetNativePair(methods, i).index, 0u);
-      CHECK(GetNativePair(methods, i).object == nullptr);
-    }
-    for (size_t i = 0; i < num_fields; ++i) {
-      CHECK_EQ(GetNativePair(fields, i).index, 0u);
-      CHECK(GetNativePair(fields, i).object == nullptr);
-    }
-    for (size_t i = 0; i < num_method_types; ++i) {
-      CHECK_EQ(method_types[i].load(std::memory_order_relaxed).index, 0u);
-      CHECK(method_types[i].load(std::memory_order_relaxed).object.IsNull());
-    }
-    for (size_t i = 0; i < dex_file->NumCallSiteIds(); ++i) {
-      CHECK(call_sites[i].IsNull());
-    }
-  }
-  if (strings != nullptr) {
-    mirror::StringDexCachePair::Initialize(strings);
-  }
-  if (types != nullptr) {
-    mirror::TypeDexCachePair::Initialize(types);
-  }
-  if (fields != nullptr) {
-    mirror::FieldDexCachePair::Initialize(fields);
-  }
-  if (methods != nullptr) {
-    mirror::MethodDexCachePair::Initialize(methods);
-  }
-  if (method_types != nullptr) {
-    mirror::MethodTypeDexCachePair::Initialize(method_types);
-  }
   SetDexFile(dex_file);
-  SetNativeArrays(strings,
-                  num_strings,
-                  types,
-                  num_types,
-                  methods,
-                  num_methods,
-                  fields,
-                  num_fields,
-                  method_types,
-                  num_method_types,
-                  call_sites,
-                  num_call_sites);
-}
-
-void DexCache::ResetNativeFields() {
-  SetDexFile(nullptr);
-  SetNativeArrays(nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0);
+  SetClassLoader(class_loader);
 }
 
 void DexCache::VisitReflectiveTargets(ReflectiveValueVisitor* visitor) {
@@ -184,59 +91,19 @@
   }
 }
 
-bool DexCache::AddPreResolvedStringsArray() {
-  DCHECK_EQ(NumPreResolvedStrings(), 0u);
-  Thread* const self = Thread::Current();
-  LinearAlloc* linear_alloc = Runtime::Current()->GetLinearAlloc();
-  const size_t num_strings = GetDexFile()->NumStringIds();
-  if (num_strings != 0) {
-    GcRoot<mirror::String>* strings =
-        linear_alloc->AllocArray<GcRoot<mirror::String>>(self, num_strings);
-    if (strings == nullptr) {
-      // Failed to allocate pre-resolved string array (probably due to address fragmentation), bail.
-      return false;
-    }
-    SetField32<false>(NumPreResolvedStringsOffset(), num_strings);
-
-    CHECK(strings != nullptr);
-    SetPreResolvedStrings(strings);
-    for (size_t i = 0; i < GetDexFile()->NumStringIds(); ++i) {
-      CHECK(GetPreResolvedStrings()[i].Read() == nullptr);
-    }
-  }
-  return true;
-}
-
-void DexCache::SetNativeArrays(StringDexCacheType* strings,
-                               uint32_t num_strings,
-                               TypeDexCacheType* resolved_types,
-                               uint32_t num_resolved_types,
-                               MethodDexCacheType* resolved_methods,
-                               uint32_t num_resolved_methods,
-                               FieldDexCacheType* resolved_fields,
-                               uint32_t num_resolved_fields,
-                               MethodTypeDexCacheType* resolved_method_types,
-                               uint32_t num_resolved_method_types,
-                               GcRoot<CallSite>* resolved_call_sites,
-                               uint32_t num_resolved_call_sites) {
-  CHECK_EQ(num_strings != 0u, strings != nullptr);
-  CHECK_EQ(num_resolved_types != 0u, resolved_types != nullptr);
-  CHECK_EQ(num_resolved_methods != 0u, resolved_methods != nullptr);
-  CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr);
-  CHECK_EQ(num_resolved_method_types != 0u, resolved_method_types != nullptr);
-  CHECK_EQ(num_resolved_call_sites != 0u, resolved_call_sites != nullptr);
-  SetStrings(strings);
-  SetResolvedTypes(resolved_types);
-  SetResolvedMethods(resolved_methods);
-  SetResolvedFields(resolved_fields);
-  SetResolvedMethodTypes(resolved_method_types);
-  SetResolvedCallSites(resolved_call_sites);
-  SetField32<false>(NumStringsOffset(), num_strings);
-  SetField32<false>(NumResolvedTypesOffset(), num_resolved_types);
-  SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods);
-  SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields);
-  SetField32<false>(NumResolvedMethodTypesOffset(), num_resolved_method_types);
-  SetField32<false>(NumResolvedCallSitesOffset(), num_resolved_call_sites);
+void DexCache::ResetNativeArrays() {
+  SetStrings(nullptr);
+  SetResolvedTypes(nullptr);
+  SetResolvedMethods(nullptr);
+  SetResolvedFields(nullptr);
+  SetResolvedMethodTypes(nullptr);
+  SetResolvedCallSites(nullptr);
+  SetField32<false>(NumStringsOffset(), 0);
+  SetField32<false>(NumResolvedTypesOffset(), 0);
+  SetField32<false>(NumResolvedMethodsOffset(), 0);
+  SetField32<false>(NumResolvedFieldsOffset(), 0);
+  SetField32<false>(NumResolvedMethodTypesOffset(), 0);
+  SetField32<false>(NumResolvedCallSitesOffset(), 0);
 }
 
 void DexCache::SetLocation(ObjPtr<mirror::String> location) {
@@ -247,6 +114,10 @@
   SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, class_loader_), class_loader);
 }
 
+ObjPtr<ClassLoader> DexCache::GetClassLoader() {
+  return GetFieldObject<ClassLoader>(OFFSET_OF_OBJECT_MEMBER(DexCache, class_loader_));
+}
+
 #if !defined(__aarch64__) && !defined(__x86_64__)
 static pthread_mutex_t dex_cache_slow_atomic_mutex = PTHREAD_MUTEX_INITIALIZER;
 
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index dd05ddd..874e6e1 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_MIRROR_DEX_CACHE_H_
 
 #include "array.h"
+#include "base/array_ref.h"
 #include "base/bit_utils.h"
 #include "base/locks.h"
 #include "dex/dex_file_types.h"
@@ -186,29 +187,13 @@
     return sizeof(DexCache);
   }
 
-  // Initialize native fields and allocate memory.
-  void InitializeNativeFields(const DexFile* dex_file, LinearAlloc* linear_alloc)
+  void Initialize(const DexFile* dex_file, ObjPtr<ClassLoader> class_loader)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(Locks::dex_lock_);
 
-  // Clear all native fields.
-  void ResetNativeFields() REQUIRES_SHARED(Locks::mutator_lock_);
-
-  template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
-  void FixupStrings(StringDexCacheType* dest, const Visitor& visitor)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
-  void FixupResolvedTypes(TypeDexCacheType* dest, const Visitor& visitor)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
-  void FixupResolvedMethodTypes(MethodTypeDexCacheType* dest, const Visitor& visitor)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
-  void FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest, const Visitor& visitor)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  // Zero all array references.
+  // WARNING: This does not free the memory since it is in LinearAlloc.
+  void ResetNativeArrays() REQUIRES_SHARED(Locks::mutator_lock_);
 
   ObjPtr<String> GetLocation() REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -278,14 +263,6 @@
   void SetResolvedString(dex::StringIndex string_idx, ObjPtr<mirror::String> resolved) ALWAYS_INLINE
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void SetPreResolvedString(dex::StringIndex string_idx,
-                            ObjPtr<mirror::String> resolved)
-      ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
-
-  // Clear the preresolved string cache to prevent further usage.
-  void ClearPreResolvedStrings()
-      ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Clear a string for a string_idx, used to undo string intern transactions to make sure
   // the string isn't kept live.
   void ClearString(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -329,21 +306,10 @@
     return GetFieldPtr64<StringDexCacheType*, kVerifyFlags>(StringsOffset());
   }
 
-  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
-  GcRoot<mirror::String>* GetPreResolvedStrings() ALWAYS_INLINE
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    return GetFieldPtr64<GcRoot<mirror::String>*, kVerifyFlags>(PreResolvedStringsOffset());
-  }
-
   void SetStrings(StringDexCacheType* strings) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     SetFieldPtr<false>(StringsOffset(), strings);
   }
 
-  void SetPreResolvedStrings(GcRoot<mirror::String>* strings)
-      ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
-    SetFieldPtr<false>(PreResolvedStringsOffset(), strings);
-  }
-
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   TypeDexCacheType* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     return GetFieldPtr<TypeDexCacheType*, kVerifyFlags>(ResolvedTypesOffset());
@@ -464,27 +430,17 @@
   uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_);
   uint32_t MethodTypeSlotIndex(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Returns true if we succeeded in adding the pre-resolved string array.
-  bool AddPreResolvedStringsArray() REQUIRES_SHARED(Locks::mutator_lock_);
-
   void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) REQUIRES(Locks::mutator_lock_);
 
   void SetClassLoader(ObjPtr<ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  ObjPtr<ClassLoader> GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
-  void SetNativeArrays(StringDexCacheType* strings,
-                       uint32_t num_strings,
-                       TypeDexCacheType* resolved_types,
-                       uint32_t num_resolved_types,
-                       MethodDexCacheType* resolved_methods,
-                       uint32_t num_resolved_methods,
-                       FieldDexCacheType* resolved_fields,
-                       uint32_t num_resolved_fields,
-                       MethodTypeDexCacheType* resolved_method_types,
-                       uint32_t num_resolved_method_types,
-                       GcRoot<CallSite>* resolved_call_sites,
-                       uint32_t num_resolved_call_sites)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  // Allocate new array in linear alloc and save it in the given fields.
+  template<typename T, size_t kMaxCacheSize>
+  T* AllocArray(MemberOffset obj_offset, MemberOffset num_offset, size_t num)
+     REQUIRES_SHARED(Locks::mutator_lock_);
 
   // std::pair<> is not trivially copyable and as such it is unsuitable for atomic operations,
   // so we use a custom pair class for loading and storing the NativeDexCachePair<>.
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 0728bab..b89b20d 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -45,21 +45,15 @@
   ASSERT_TRUE(java_lang_dex_file_ != nullptr);
   Handle<DexCache> dex_cache(
       hs.NewHandle(class_linker_->AllocAndInitializeDexCache(
-          soa.Self(),
-          *java_lang_dex_file_,
-          Runtime::Current()->GetLinearAlloc())));
+          soa.Self(), *java_lang_dex_file_, /*class_loader=*/nullptr)));
   ASSERT_TRUE(dex_cache != nullptr);
 
-  EXPECT_TRUE(dex_cache->StaticStringSize() == dex_cache->NumStrings()
-      || java_lang_dex_file_->NumStringIds() == dex_cache->NumStrings());
-  EXPECT_TRUE(dex_cache->StaticTypeSize() == dex_cache->NumResolvedTypes()
-      || java_lang_dex_file_->NumTypeIds() == dex_cache->NumResolvedTypes());
-  EXPECT_TRUE(dex_cache->StaticMethodSize() == dex_cache->NumResolvedMethods()
-      || java_lang_dex_file_->NumMethodIds() == dex_cache->NumResolvedMethods());
-  EXPECT_TRUE(dex_cache->StaticArtFieldSize() == dex_cache->NumResolvedFields()
-      || java_lang_dex_file_->NumFieldIds() ==  dex_cache->NumResolvedFields());
-  EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes()
-      || java_lang_dex_file_->NumProtoIds() == dex_cache->NumResolvedMethodTypes());
+  // The cache is initially empty.
+  EXPECT_EQ(0u, dex_cache->NumStrings());
+  EXPECT_EQ(0u, dex_cache->NumResolvedTypes());
+  EXPECT_EQ(0u, dex_cache->NumResolvedMethods());
+  EXPECT_EQ(0u, dex_cache->NumResolvedFields());
+  EXPECT_EQ(0u, dex_cache->NumResolvedMethodTypes());
 }
 
 TEST_F(DexCacheMethodHandlesTest, Open) {
@@ -68,26 +62,9 @@
   ASSERT_TRUE(java_lang_dex_file_ != nullptr);
   Handle<DexCache> dex_cache(
       hs.NewHandle(class_linker_->AllocAndInitializeDexCache(
-          soa.Self(),
-          *java_lang_dex_file_,
-          Runtime::Current()->GetLinearAlloc())));
+          soa.Self(), *java_lang_dex_file_, /*class_loader=*/nullptr)));
 
-  EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes()
-      || java_lang_dex_file_->NumProtoIds() == dex_cache->NumResolvedMethodTypes());
-}
-
-TEST_F(DexCacheTest, LinearAlloc) {
-  ScopedObjectAccess soa(Thread::Current());
-  jobject jclass_loader(LoadDex("Main"));
-  ASSERT_TRUE(jclass_loader != nullptr);
-  StackHandleScope<1> hs(soa.Self());
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader>(jclass_loader)));
-  ObjPtr<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()));
+  EXPECT_EQ(0u, dex_cache->NumResolvedMethodTypes());
 }
 
 TEST_F(DexCacheTest, TestResolvedFieldAccess) {