Fix compiler crash due to inline caches and improve docs

Fix a potential crash when extracting the inline caches from the method.
The way we copied the inline cache into a new map was not correct. In
ProfileCompilationInfo::GetMethod() we reused the same profile arena
for allocation which is not thread safe. When compiling with multiple
threads the profile arena could become corrupted due to races.

Address all the comments from the late reviews on the CL which migrates
the profiles to arena storage.

Test: m test-art-host
      compile with speed-profile apps on device
Bug: 37711886
Bug: 62062532
Change-Id: I61af5175bc68b2c7dba77afb3cdff221989cc387
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 8f5c51f..3e6b845 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -578,7 +578,6 @@
     return kInlineCacheNoData;
   }
 
-  // Use the profile arena when extracting the method info.
   std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_profile =
       pci->GetMethod(caller_dex_file.GetLocation(),
                      caller_dex_file.GetLocationChecksum(),
@@ -603,8 +602,8 @@
     const ProfileCompilationInfo::OfflineProfileMethodInfo& offline_profile,
     /*out*/Handle<mirror::ObjectArray<mirror::Class>> inline_cache)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  const auto it = offline_profile.inline_caches.find(invoke_instruction->GetDexPc());
-  if (it == offline_profile.inline_caches.end()) {
+  const auto it = offline_profile.inline_caches->find(invoke_instruction->GetDexPc());
+  if (it == offline_profile.inline_caches->end()) {
     return kInlineCacheUninitialized;
   }
 
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 8bdc576..a35b199 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -2130,9 +2130,10 @@
 
   bool LoadProfile() {
     DCHECK(UseProfile());
-    // TODO(calin): We should be using the runtime arena pool (instead of the default profile arean).
-    // However the setup logic is messy and needs cleaning up before that (e.g. the oat writers are
-    // created before the runtime).
+    // TODO(calin): We should be using the runtime arena pool (instead of the
+    // default profile arena). However the setup logic is messy and needs
+    // cleaning up before that (e.g. the oat writers are created before the
+    // runtime).
     profile_compilation_info_.reset(new ProfileCompilationInfo());
     ScopedFlock flock;
     bool success = true;
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index b836632..41b9f99 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -32,7 +32,7 @@
 
 class ProfileAssistantTest : public CommonRuntimeTest {
  public:
-  virtual void PostRuntimeCreate() {
+  void PostRuntimeCreate() OVERRIDE {
     arena_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
   }
 
@@ -72,10 +72,18 @@
     ASSERT_TRUE(profile.GetFile()->ResetOffset());
   }
 
+  // Creates an inline cache which will be destructed at the end of the test.
+  ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() {
+    used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap(
+        std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)));
+    return used_inline_caches.back().get();
+  }
+
   ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo(
         const std::string& dex_location1, uint32_t dex_checksum1,
         const std::string& dex_location2, uint32_t dex_checksum2) {
-    ProfileCompilationInfo::OfflineProfileMethodInfo pmi(arena_.get());
+    ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
+    ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
     pmi.dex_references.emplace_back(dex_location1, dex_checksum1);
     pmi.dex_references.emplace_back(dex_location2, dex_checksum2);
 
@@ -83,7 +91,7 @@
     for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
       ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.AddClass(0, dex::TypeIndex(0));
-      pmi.inline_caches.Put(dex_pc, dex_pc_data);
+      ic_map->Put(dex_pc, dex_pc_data);
     }
     // Polymorphic
     for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
@@ -91,19 +99,19 @@
       dex_pc_data.AddClass(0, dex::TypeIndex(0));
       dex_pc_data.AddClass(1, dex::TypeIndex(1));
 
-      pmi.inline_caches.Put(dex_pc, dex_pc_data);
+      ic_map->Put(dex_pc, dex_pc_data);
     }
     // Megamorphic
     for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
       ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.SetIsMegamorphic();
-      pmi.inline_caches.Put(dex_pc, dex_pc_data);
+      ic_map->Put(dex_pc, dex_pc_data);
     }
     // Missing types
     for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
       ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.SetIsMissingTypes();
-      pmi.inline_caches.Put(dex_pc, dex_pc_data);
+      ic_map->Put(dex_pc, dex_pc_data);
     }
 
     return pmi;
@@ -258,8 +266,8 @@
                        method->GetDexFile()->GetLocationChecksum(),
                        method->GetDexMethodIndex());
     ASSERT_TRUE(pmi != nullptr);
-    ASSERT_EQ(pmi->inline_caches.size(), 1u);
-    ProfileCompilationInfo::DexPcData dex_pc_data = pmi->inline_caches.begin()->second;
+    ASSERT_EQ(pmi->inline_caches->size(), 1u);
+    const ProfileCompilationInfo::DexPcData& dex_pc_data = pmi->inline_caches->begin()->second;
 
     ASSERT_EQ(dex_pc_data.is_megamorphic, is_megamorphic);
     ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types);
@@ -280,6 +288,11 @@
   }
 
   std::unique_ptr<ArenaAllocator> arena_;
+
+  // Cache of inline caches generated during tests.
+  // This makes it easier to pass data between different utilities and ensure that
+  // caches are destructed at the end of the test.
+  std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
 };
 
 TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
@@ -702,7 +715,7 @@
                        no_inline_cache->GetDexFile()->GetLocationChecksum(),
                        no_inline_cache->GetDexMethodIndex());
     ASSERT_TRUE(pmi_no_inline_cache != nullptr);
-    ASSERT_TRUE(pmi_no_inline_cache->inline_caches.empty());
+    ASSERT_TRUE(pmi_no_inline_cache->inline_caches->empty());
   }
 }
 
diff --git a/profman/profman.cc b/profman/profman.cc
index 26e7e46..366b4de 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -423,16 +423,13 @@
     }
     for (const std::unique_ptr<const DexFile>& dex_file : *dex_files) {
       std::set<dex::TypeIndex> class_types;
-      ProfileCompilationInfo::MethodMap methods(std::less<uint16_t>(),
-                                                profile_info.GetArena()->Adapter());
-      if (profile_info.GetClassesAndMethods(dex_file.get(), &class_types, &methods)) {
+      std::set<uint16_t> methods;
+      if (profile_info.GetClassesAndMethods(*dex_file.get(), &class_types, &methods)) {
         for (const dex::TypeIndex& type_index : class_types) {
           const DexFile::TypeId& type_id = dex_file->GetTypeId(type_index);
           out_lines->insert(std::string(dex_file->GetTypeDescriptor(type_id)));
         }
-        for (const auto& pair : methods) {
-          // TODO: Process inline caches.
-          const uint16_t dex_method_idx = pair.first;
+        for (uint16_t dex_method_idx : methods) {
           const DexFile::MethodId& id = dex_file->GetMethodId(dex_method_idx);
           std::string signature_string(dex_file->GetMethodSignature(id).ToString());
           std::string type_string(dex_file->GetTypeDescriptor(dex_file->GetTypeId(id.class_idx_)));
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 9b80ad7..10ab62f 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -69,21 +69,21 @@
               "InlineCache::kIndividualCacheSize is larger than expected");
 
 ProfileCompilationInfo::ProfileCompilationInfo(ArenaPool* custom_arena_pool)
-    : default_arena_pool_(nullptr),
-      arena_(new ArenaAllocator(custom_arena_pool)),
-      info_(arena_->Adapter(kArenaAllocProfile)),
-      profile_key_map_(std::less<const std::string>(), arena_->Adapter(kArenaAllocProfile)) {
+    : default_arena_pool_(),
+      arena_(custom_arena_pool),
+      info_(arena_.Adapter(kArenaAllocProfile)),
+      profile_key_map_(std::less<const std::string>(), arena_.Adapter(kArenaAllocProfile)) {
 }
 
 ProfileCompilationInfo::ProfileCompilationInfo()
-    : default_arena_pool_(new ArenaPool(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo")),
-      arena_(new ArenaAllocator(default_arena_pool_.get())),
-      info_(arena_->Adapter(kArenaAllocProfile)),
-      profile_key_map_(std::less<const std::string>(), arena_->Adapter(kArenaAllocProfile)) {
+    : default_arena_pool_(/*use_malloc*/true, /*low_4gb*/false, "ProfileCompilationInfo"),
+      arena_(&default_arena_pool_),
+      info_(arena_.Adapter(kArenaAllocProfile)),
+      profile_key_map_(std::less<const std::string>(), arena_.Adapter(kArenaAllocProfile)) {
 }
 
 ProfileCompilationInfo::~ProfileCompilationInfo() {
-  VLOG(profiler) << Dumpable<MemStats>(arena_->GetMemStats());
+  VLOG(profiler) << Dumpable<MemStats>(arena_.GetMemStats());
   for (DexFileData* data : info_) {
     delete data;
   }
@@ -94,11 +94,27 @@
   if (is_megamorphic || is_missing_types) {
     return;
   }
-  classes.emplace(dex_profile_idx, type_idx);
-  if (classes.size() >= InlineCache::kIndividualCacheSize) {
+
+  // Perform an explicit lookup for the type instead of directly emplacing the
+  // element. We do this because emplace() allocates the node before doing the
+  // lookup and if it then finds an identical element, it shall deallocate the
+  // node. For Arena allocations, that's essentially a leak.
+  ClassReference ref(dex_profile_idx, type_idx);
+  auto it = classes.find(ref);
+  if (it != classes.end()) {
+    // The type index exists.
+    return;
+  }
+
+  // Check if the adding the type will cause the cache to become megamorphic.
+  if (classes.size() + 1 >= InlineCache::kIndividualCacheSize) {
     is_megamorphic = true;
     classes.clear();
+    return;
   }
+
+  // The type does not exist and the inline cache will not be megamorphic.
+  classes.insert(ref);
 }
 
 // Transform the actual dex location into relative paths.
@@ -475,8 +491,8 @@
   uint8_t profile_index = profile_index_it->second;
   if (info_.size() <= profile_index) {
     // This is a new addition. Add it to the info_ array.
-    DexFileData* dex_file_data = new (arena_.get()) DexFileData(
-        arena_.get(), profile_key, checksum, profile_index);
+    DexFileData* dex_file_data = new (&arena_) DexFileData(
+        &arena_, profile_key, checksum, profile_index);
     info_.push_back(dex_file_data);
   }
   DexFileData* result = info_[profile_index];
@@ -523,7 +539,7 @@
 bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location,
                                             uint32_t dex_checksum,
                                             uint16_t method_index) {
-  return AddMethod(dex_location, dex_checksum, method_index, OfflineProfileMethodInfo(arena_.get()));
+  return AddMethod(dex_location, dex_checksum, method_index, OfflineProfileMethodInfo(nullptr));
 }
 
 bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
@@ -534,8 +550,14 @@
   if (data == nullptr) {  // checksum mismatch
     return false;
   }
+  // Add the method.
   InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
-  for (const auto& pmi_inline_cache_it : pmi.inline_caches) {
+
+  if (pmi.inline_caches == nullptr) {
+    // If we don't have inline caches return success right away.
+    return true;
+  }
+  for (const auto& pmi_inline_cache_it : *pmi.inline_caches) {
     uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first;
     const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second;
     DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, pmi_ic_dex_pc);
@@ -1167,7 +1189,8 @@
   if (inline_caches == nullptr) {
     return nullptr;
   }
-  std::unique_ptr<OfflineProfileMethodInfo> pmi(new OfflineProfileMethodInfo(arena_.get()));
+
+  std::unique_ptr<OfflineProfileMethodInfo> pmi(new OfflineProfileMethodInfo(inline_caches));
 
   pmi->dex_references.resize(info_.size());
   for (const DexFileData* dex_data : info_) {
@@ -1175,8 +1198,6 @@
     pmi->dex_references[dex_data->profile_index].dex_checksum = dex_data->checksum;
   }
 
-  // TODO(calin): maybe expose a direct pointer to avoid copying
-  pmi->inline_caches = *inline_caches;
   return pmi;
 }
 
@@ -1293,16 +1314,18 @@
   return os.str();
 }
 
-bool ProfileCompilationInfo::GetClassesAndMethods(const DexFile* dex_file,
+bool ProfileCompilationInfo::GetClassesAndMethods(const DexFile& dex_file,
                                                   std::set<dex::TypeIndex>* class_set,
-                                                  MethodMap* method_map) const {
+                                                  std::set<uint16_t>* method_set) const {
   std::set<std::string> ret;
-  std::string profile_key = GetProfileDexFileKey(dex_file->GetLocation());
+  std::string profile_key = GetProfileDexFileKey(dex_file.GetLocation());
   const DexFileData* dex_data = FindDexData(profile_key);
-  if (dex_data == nullptr || dex_data->checksum != dex_file->GetLocationChecksum()) {
+  if (dex_data == nullptr || dex_data->checksum != dex_file.GetLocationChecksum()) {
     return false;
   }
-  *method_map = dex_data->method_map;
+  for (const auto& it : dex_data->method_map) {
+    method_set->insert(it.first);
+  }
   for (const dex::TypeIndex& type_index : dex_data->class_set) {
     class_set->insert(type_index);
   }
@@ -1415,17 +1438,17 @@
 
 bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
       const OfflineProfileMethodInfo& other) const {
-  if (inline_caches.size() != other.inline_caches.size()) {
+  if (inline_caches->size() != other.inline_caches->size()) {
     return false;
   }
 
   // We can't use a simple equality test because we need to match the dex files
   // of the inline caches which might have different profile indexes.
-  for (const auto& inline_cache_it : inline_caches) {
+  for (const auto& inline_cache_it : *inline_caches) {
     uint16_t dex_pc = inline_cache_it.first;
     const DexPcData dex_pc_data = inline_cache_it.second;
-    const auto other_it = other.inline_caches.find(dex_pc);
-    if (other_it == other.inline_caches.end()) {
+    const auto& other_it = other.inline_caches->find(dex_pc);
+    if (other_it == other.inline_caches->end()) {
       return false;
     }
     const DexPcData& other_dex_pc_data = other_it->second;
@@ -1468,7 +1491,7 @@
 
 ProfileCompilationInfo::DexPcData*
 ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc) {
-  return &(inline_cache->FindOrAdd(dex_pc, DexPcData(arena_.get()))->second);
+  return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&arena_))->second);
 }
 
 }  // namespace art
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 6756352..09f1548 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -38,7 +38,7 @@
 struct ProfileMethodInfo {
   struct ProfileClassReference {
     ProfileClassReference() : dex_file(nullptr) {}
-    ProfileClassReference(const DexFile* dex, const dex::TypeIndex& index)
+    ProfileClassReference(const DexFile* dex, const dex::TypeIndex index)
         : dex_file(dex), type_index(index) {}
 
     const DexFile* dex_file;
@@ -117,7 +117,7 @@
   // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
   // and one from split-B.
   struct ClassReference : public ValueObject {
-    ClassReference(uint8_t dex_profile_idx, const dex::TypeIndex& type_idx) :
+    ClassReference(uint8_t dex_profile_idx, const dex::TypeIndex type_idx) :
       dex_profile_index(dex_profile_idx), type_index(type_idx) {}
 
     bool operator==(const ClassReference& other) const {
@@ -180,13 +180,13 @@
   // i.e. the dex file of any ClassReference present in the inline caches can be found at
   // dex_references[ClassReference::dex_profile_index].
   struct OfflineProfileMethodInfo {
-    explicit OfflineProfileMethodInfo(ArenaAllocator* allocator)
-        : inline_caches(std::less<uint16_t>(), allocator->Adapter(kArenaAllocProfile)) {}
+    explicit OfflineProfileMethodInfo(const InlineCacheMap* inline_cache_map)
+        : inline_caches(inline_cache_map) {}
 
     bool operator==(const OfflineProfileMethodInfo& other) const;
 
+    const InlineCacheMap* const inline_caches;
     std::vector<DexReference> dex_references;
-    InlineCacheMap inline_caches;
   };
 
   // Public methods to create, extend or query the profile.
@@ -232,7 +232,8 @@
 
   // Return the method data for the given location and index from the profiling info.
   // If the method index is not found or the checksum doesn't match, null is returned.
-  // The allocations for the method info are done on the current profile arena.
+  // Note: the inline cache map is a pointer to the map stored in the profile and
+  // its allocation will go away if the profile goes out of scope.
   std::unique_ptr<OfflineProfileMethodInfo> GetMethod(const std::string& dex_location,
                                                       uint32_t dex_checksum,
                                                       uint16_t dex_method_index) const;
@@ -246,12 +247,12 @@
   std::string DumpInfo(const std::vector<const DexFile*>* dex_files,
                        bool print_full_dex_location = true) const;
 
-  // Return the classes and methods for a given dex file through out args. The otu args are the set
+  // Return the classes and methods for a given dex file through out args. The out args are the set
   // of class as well as the methods and their associated inline caches. Returns true if the dex
   // file is register and has a matching checksum, false otherwise.
-  bool GetClassesAndMethods(const DexFile* dex_file,
-                            std::set<dex::TypeIndex>* class_set,
-                            MethodMap* method_map) const;
+  bool GetClassesAndMethods(const DexFile& dex_file,
+                            /*out*/std::set<dex::TypeIndex>* class_set,
+                            /*out*/std::set<uint16_t>* method_set) const;
 
   // Perform an equality test with the `other` profile information.
   bool Equals(const ProfileCompilationInfo& other);
@@ -281,7 +282,7 @@
   static bool Equals(const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi1,
                      const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi2);
 
-  ArenaAllocator* GetArena() { return arena_.get(); }
+  ArenaAllocator* GetArena() { return &arena_; }
 
  private:
   enum ProfileLoadSatus {
@@ -512,8 +513,8 @@
   friend class ProfileAssistantTest;
   friend class Dex2oatLayoutTest;
 
-  std::unique_ptr<ArenaPool> default_arena_pool_;
-  std::unique_ptr<ArenaAllocator> arena_;
+  ArenaPool default_arena_pool_;
+  ArenaAllocator arena_;
 
   // Vector containing the actual profile info.
   // The vector index is the profile index of the dex data and
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index a054199..b0fceee 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -33,7 +33,7 @@
 
 class ProfileCompilationInfoTest : public CommonRuntimeTest {
  public:
-  virtual void PostRuntimeCreate() {
+  void PostRuntimeCreate() OVERRIDE {
     arena_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
   }
 
@@ -162,13 +162,21 @@
     return info.Save(filename, nullptr);
   }
 
+  // Creates an inline cache which will be destructed at the end of the test.
+  ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() {
+    used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap(
+        std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)));
+    return used_inline_caches.back().get();
+  }
+
   ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo(
         const ProfileMethodInfo& pmi) {
-    ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(arena_.get());
+    ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
+    ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map);
     SafeMap<DexFile*, uint8_t> dex_map;  // dex files to profile index
     for (const auto& inline_cache : pmi.inline_caches) {
       ProfileCompilationInfo::DexPcData& dex_pc_data =
-          offline_pmi.inline_caches.FindOrAdd(
+          ic_map->FindOrAdd(
               inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(arena_.get()))->second;
       if (inline_cache.is_missing_types) {
         dex_pc_data.SetIsMissingTypes();
@@ -191,17 +199,12 @@
 
   // Creates an offline profile used for testing inline caches.
   ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() {
-    ProfileCompilationInfo::OfflineProfileMethodInfo pmi(arena_.get());
-
-    pmi.dex_references.emplace_back("dex_location1", /* checksum */1);
-    pmi.dex_references.emplace_back("dex_location2", /* checksum */2);
-    pmi.dex_references.emplace_back("dex_location3", /* checksum */3);
-
+    ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
     // Monomorphic
     for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
       ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.AddClass(0, dex::TypeIndex(0));
-      pmi.inline_caches.Put(dex_pc, dex_pc_data);
+      ic_map->Put(dex_pc, dex_pc_data);
     }
     // Polymorphic
     for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
@@ -210,26 +213,34 @@
       dex_pc_data.AddClass(1, dex::TypeIndex(1));
       dex_pc_data.AddClass(2, dex::TypeIndex(2));
 
-      pmi.inline_caches.Put(dex_pc, dex_pc_data);
+      ic_map->Put(dex_pc, dex_pc_data);
     }
     // Megamorphic
     for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
       ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.SetIsMegamorphic();
-      pmi.inline_caches.Put(dex_pc, dex_pc_data);
+      ic_map->Put(dex_pc, dex_pc_data);
     }
     // Missing types
     for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
       ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.SetIsMissingTypes();
-      pmi.inline_caches.Put(dex_pc, dex_pc_data);
+      ic_map->Put(dex_pc, dex_pc_data);
     }
 
+    ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
+
+    pmi.dex_references.emplace_back("dex_location1", /* checksum */1);
+    pmi.dex_references.emplace_back("dex_location2", /* checksum */2);
+    pmi.dex_references.emplace_back("dex_location3", /* checksum */3);
+
     return pmi;
   }
 
   void MakeMegamorphic(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) {
-    for (auto it : pmi->inline_caches) {
+    ProfileCompilationInfo::InlineCacheMap* ic_map =
+        const_cast<ProfileCompilationInfo::InlineCacheMap*>(pmi->inline_caches);
+    for (auto it : *ic_map) {
       for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) {
         it.second.AddClass(0, dex::TypeIndex(k));
       }
@@ -237,7 +248,9 @@
   }
 
   void SetIsMissingTypes(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) {
-    for (auto it : pmi->inline_caches) {
+    ProfileCompilationInfo::InlineCacheMap* ic_map =
+        const_cast<ProfileCompilationInfo::InlineCacheMap*>(pmi->inline_caches);
+    for (auto it : *ic_map) {
       it.second.SetIsMissingTypes();
     }
   }
@@ -248,6 +261,11 @@
   static constexpr int kProfileVersionSize = 4;
 
   std::unique_ptr<ArenaAllocator> arena_;
+
+  // Cache of inline caches generated during tests.
+  // This makes it easier to pass data between different utilities and ensure that
+  // caches are destructed at the end of the test.
+  std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
 };
 
 TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
@@ -673,24 +691,26 @@
   ProfileCompilationInfo info;
   ProfileCompilationInfo info_reindexed;
 
-  ProfileCompilationInfo::OfflineProfileMethodInfo pmi(arena_.get());
+  ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
+  ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
   pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
   pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2);
   for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
     ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
     dex_pc_data.AddClass(0, dex::TypeIndex(0));
     dex_pc_data.AddClass(1, dex::TypeIndex(1));
-    pmi.inline_caches.Put(dex_pc, dex_pc_data);
+    ic_map->Put(dex_pc, dex_pc_data);
   }
 
-  ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed(arena_.get());
+  ProfileCompilationInfo::InlineCacheMap* ic_map_reindexed = CreateInlineCacheMap();
+  ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed(ic_map_reindexed);
   pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2);
   pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1);
   for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
     ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
     dex_pc_data.AddClass(1, dex::TypeIndex(0));
     dex_pc_data.AddClass(0, dex::TypeIndex(1));
-    pmi_reindexed.inline_caches.Put(dex_pc, dex_pc_data);
+    ic_map_reindexed->Put(dex_pc, dex_pc_data);
   }
 
   // Profile 1 and Profile 2 get the same methods but in different order.
@@ -738,11 +758,12 @@
 
 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) {
   // Create a megamorphic inline cache.
-  ProfileCompilationInfo::OfflineProfileMethodInfo pmi(arena_.get());
+  ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
+  ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
   pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
   ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
   dex_pc_data.SetIsMegamorphic();
-  pmi.inline_caches.Put(/*dex_pc*/ 0, dex_pc_data);
+  ic_map->Put(/*dex_pc*/ 0, dex_pc_data);
 
   ProfileCompilationInfo info_megamorphic;
   ASSERT_TRUE(AddMethod("dex_location1",
@@ -767,11 +788,12 @@
 
 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCachesMerge) {
   // Create an inline cache with missing types
-  ProfileCompilationInfo::OfflineProfileMethodInfo pmi(arena_.get());
+  ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
+  ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
   pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
   ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
   dex_pc_data.SetIsMissingTypes();
-  pmi.inline_caches.Put(/*dex_pc*/ 0, dex_pc_data);
+  ic_map->Put(/*dex_pc*/ 0, dex_pc_data);
 
   ProfileCompilationInfo info_megamorphic;
   ASSERT_TRUE(AddMethod("dex_location1",
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 0c94a94..166b6f4 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -259,7 +259,9 @@
                        << " (" << classes.GetDexLocation() << ")";
       }
     }
-    auto info_it = profile_cache_.Put(filename, new ProfileCompilationInfo(Runtime::Current()->GetArenaPool()));
+    auto info_it = profile_cache_.Put(
+        filename,
+        new ProfileCompilationInfo(Runtime::Current()->GetArenaPool()));
 
     ProfileCompilationInfo* cached_info = info_it->second;
     cached_info->AddMethodsAndClasses(profile_methods_for_location,
@@ -366,6 +368,8 @@
         total_number_of_failed_writes_++;
       }
     }
+    // Trim the maps to madvise the pages used for profile info.
+    // It is unlikely we will need them again in the near feature.
     Runtime::Current()->GetArenaPool()->TrimMaps();
   }