Use arena allocation for profiles

By using our arena allocator we can madvise the memory used during
profile processing right way. jemalloc may defer the release based on
unpredictable native allocation.

The other advantage of arenas is a much simpler way to measure the
memory needed by profiles.

Test: m test-art-host
Test: manual inspection with meminfo and heaptrack
Bug: 37711886

Change-Id: Ib656a8ac63600477ff553831087a83dc40d9c537
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 8674e72..8f5c51f 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -578,12 +578,12 @@
     return kInlineCacheNoData;
   }
 
-  ProfileCompilationInfo::OfflineProfileMethodInfo offline_profile;
-  bool found = pci->GetMethod(caller_dex_file.GetLocation(),
-                              caller_dex_file.GetLocationChecksum(),
-                              caller_compilation_unit_.GetDexMethodIndex(),
-                              &offline_profile);
-  if (!found) {
+  // 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(),
+                     caller_compilation_unit_.GetDexMethodIndex());
+  if (offline_profile == nullptr) {
     return kInlineCacheNoData;  // no profile information for this invocation.
   }
 
@@ -593,7 +593,7 @@
     return kInlineCacheNoData;
   } else {
     return ExtractClassesFromOfflineProfile(invoke_instruction,
-                                            offline_profile,
+                                            *(offline_profile.get()),
                                             *inline_cache);
   }
 }
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 660409f..8bdc576 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -2130,7 +2130,9 @@
 
   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).
     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 38254e2..b836632 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -21,6 +21,7 @@
 #include "common_runtime_test.h"
 #include "exec_utils.h"
 #include "jit/profile_compilation_info.h"
+#include "linear_alloc.h"
 #include "mirror/class-inl.h"
 #include "obj_ptr-inl.h"
 #include "profile_assistant.h"
@@ -30,6 +31,11 @@
 namespace art {
 
 class ProfileAssistantTest : public CommonRuntimeTest {
+ public:
+  virtual void PostRuntimeCreate() {
+    arena_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
+  }
+
  protected:
   void SetupProfile(const std::string& id,
                     uint32_t checksum,
@@ -69,19 +75,19 @@
   ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo(
         const std::string& dex_location1, uint32_t dex_checksum1,
         const std::string& dex_location2, uint32_t dex_checksum2) {
-    ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+    ProfileCompilationInfo::OfflineProfileMethodInfo pmi(arena_.get());
     pmi.dex_references.emplace_back(dex_location1, dex_checksum1);
     pmi.dex_references.emplace_back(dex_location2, dex_checksum2);
 
     // Monomorphic
     for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
-      ProfileCompilationInfo::DexPcData dex_pc_data;
+      ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.AddClass(0, dex::TypeIndex(0));
       pmi.inline_caches.Put(dex_pc, dex_pc_data);
     }
     // Polymorphic
     for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
-      ProfileCompilationInfo::DexPcData dex_pc_data;
+      ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.AddClass(0, dex::TypeIndex(0));
       dex_pc_data.AddClass(1, dex::TypeIndex(1));
 
@@ -89,13 +95,13 @@
     }
     // Megamorphic
     for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
-      ProfileCompilationInfo::DexPcData dex_pc_data;
+      ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.SetIsMegamorphic();
       pmi.inline_caches.Put(dex_pc, dex_pc_data);
     }
     // Missing types
     for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
-      ProfileCompilationInfo::DexPcData dex_pc_data;
+      ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.SetIsMissingTypes();
       pmi.inline_caches.Put(dex_pc, dex_pc_data);
     }
@@ -247,13 +253,13 @@
                           bool is_megamorphic,
                           bool is_missing_types)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
-    ASSERT_TRUE(info.GetMethod(method->GetDexFile()->GetLocation(),
-                               method->GetDexFile()->GetLocationChecksum(),
-                               method->GetDexMethodIndex(),
-                               &pmi));
-    ASSERT_EQ(pmi.inline_caches.size(), 1u);
-    ProfileCompilationInfo::DexPcData dex_pc_data = pmi.inline_caches.begin()->second;
+    std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi =
+        info.GetMethod(method->GetDexFile()->GetLocation(),
+                       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(dex_pc_data.is_megamorphic, is_megamorphic);
     ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types);
@@ -262,7 +268,7 @@
     for (mirror::Class* it : expected_clases) {
       for (const auto& class_ref : dex_pc_data.classes) {
         ProfileCompilationInfo::DexReference dex_ref =
-            pmi.dex_references[class_ref.dex_profile_index];
+            pmi->dex_references[class_ref.dex_profile_index];
         if (dex_ref.MatchesDex(&(it->GetDexFile())) &&
             class_ref.type_index == it->GetDexTypeIndex()) {
           found++;
@@ -272,6 +278,8 @@
 
     ASSERT_EQ(expected_clases.size(), found);
   }
+
+  std::unique_ptr<ArenaAllocator> arena_;
 };
 
 TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
@@ -541,11 +549,11 @@
   for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
     if (!method.IsCopied() && method.GetCodeItem() != nullptr) {
       ++method_count;
-      ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
-      ASSERT_TRUE(info.GetMethod(method.GetDexFile()->GetLocation(),
-                                 method.GetDexFile()->GetLocationChecksum(),
-                                 method.GetDexMethodIndex(),
-                                 &pmi));
+      std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi =
+          info.GetMethod(method.GetDexFile()->GetLocation(),
+                         method.GetDexFile()->GetLocationChecksum(),
+                         method.GetDexMethodIndex());
+      ASSERT_TRUE(pmi != nullptr);
     }
   }
   EXPECT_GT(method_count, 0u);
@@ -689,12 +697,12 @@
     // Verify that method noInlineCache has no inline caches in the profile.
     ArtMethod* no_inline_cache = GetVirtualMethod(class_loader, "LTestInline;", "noInlineCache");
     ASSERT_TRUE(no_inline_cache != nullptr);
-    ProfileCompilationInfo::OfflineProfileMethodInfo pmi_no_inline_cache;
-    ASSERT_TRUE(info.GetMethod(no_inline_cache->GetDexFile()->GetLocation(),
-                               no_inline_cache->GetDexFile()->GetLocationChecksum(),
-                               no_inline_cache->GetDexMethodIndex(),
-                               &pmi_no_inline_cache));
-    ASSERT_TRUE(pmi_no_inline_cache.inline_caches.empty());
+    std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi_no_inline_cache =
+        info.GetMethod(no_inline_cache->GetDexFile()->GetLocation(),
+                       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());
   }
 }
 
diff --git a/profman/profman.cc b/profman/profman.cc
index 384e129..26e7e46 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -423,7 +423,8 @@
     }
     for (const std::unique_ptr<const DexFile>& dex_file : *dex_files) {
       std::set<dex::TypeIndex> class_types;
-      ProfileCompilationInfo::MethodMap methods;
+      ProfileCompilationInfo::MethodMap methods(std::less<uint16_t>(),
+                                                profile_info.GetArena()->Adapter());
       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);
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index fc5b5b1..f672882 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -88,6 +88,7 @@
   "CallingConv  ",
   "CHA          ",
   "Scheduler    ",
+  "Profile      ",
 };
 
 template <bool kCount>
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index 5430458..ebde82d 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -98,6 +98,7 @@
   kArenaAllocCallingConvention,
   kArenaAllocCHA,
   kArenaAllocScheduler,
+  kArenaAllocProfile,
   kNumArenaAllocKinds
 };
 
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 220f298..d1ce01b 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -31,6 +31,8 @@
 #include <zlib.h>
 #include <base/time_utils.h>
 
+#include "base/arena_allocator.h"
+#include "base/dumpable.h"
 #include "base/mutex.h"
 #include "base/scoped_flock.h"
 #include "base/stl_util.h"
@@ -66,12 +68,22 @@
 static_assert(InlineCache::kIndividualCacheSize < kIsMissingTypesEncoding,
               "InlineCache::kIndividualCacheSize is larger than expected");
 
-ProfileCompilationInfo::ProfileCompilationInfo(const ProfileCompilationInfo& pci) {
-  MergeWith(pci);
+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)) {
+}
+
+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)) {
 }
 
 ProfileCompilationInfo::~ProfileCompilationInfo() {
-  ClearProfile();
+  VLOG(profiler) << Dumpable<MemStats>(arena_->GetMemStats());
 }
 
 void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
@@ -460,7 +472,9 @@
   uint8_t profile_index = profile_index_it->second;
   if (info_.size() <= profile_index) {
     // This is a new addition. Add it to the info_ array.
-    info_.emplace_back(new DexFileData(profile_key, checksum, profile_index));
+    DexFileData* dex_file_data = new (arena_.get()) DexFileData(
+        arena_.get(), profile_key, checksum, profile_index);
+    info_.push_back(dex_file_data);
   }
   DexFileData* result = info_[profile_index];
   // DCHECK that profile info map key is consistent with the one stored in the dex file data.
@@ -506,7 +520,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());
+  return AddMethod(dex_location, dex_checksum, method_index, OfflineProfileMethodInfo(arena_.get()));
 }
 
 bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
@@ -517,22 +531,22 @@
   if (data == nullptr) {  // checksum mismatch
     return false;
   }
-  auto inline_cache_it = data->method_map.FindOrAdd(method_index);
+  InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
   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 = inline_cache_it->second.FindOrAdd(pmi_ic_dex_pc)->second;
-    if (dex_pc_data.is_missing_types || dex_pc_data.is_megamorphic) {
+    DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, pmi_ic_dex_pc);
+    if (dex_pc_data->is_missing_types || dex_pc_data->is_megamorphic) {
       // We are already megamorphic or we are missing types; no point in going forward.
       continue;
     }
 
     if (pmi_ic_dex_pc_data.is_missing_types) {
-      dex_pc_data.SetIsMissingTypes();
+      dex_pc_data->SetIsMissingTypes();
       continue;
     }
     if (pmi_ic_dex_pc_data.is_megamorphic) {
-      dex_pc_data.SetIsMegamorphic();
+      dex_pc_data->SetIsMegamorphic();
       continue;
     }
 
@@ -544,7 +558,7 @@
       if (class_dex_data == nullptr) {  // checksum mismatch
         return false;
       }
-      dex_pc_data.AddClass(class_dex_data->profile_index, class_ref.type_index);
+      dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.type_index);
     }
   }
   return true;
@@ -557,12 +571,11 @@
   if (data == nullptr) {  // checksum mismatch
     return false;
   }
-  auto inline_cache_it = data->method_map.FindOrAdd(pmi.dex_method_index);
+  InlineCacheMap* inline_cache = data->FindOrAddMethod(pmi.dex_method_index);
 
   for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
     if (cache.is_missing_types) {
-      auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(cache.dex_pc);
-      dex_pc_data_it->second.SetIsMissingTypes();
+      FindOrAddDexPc(inline_cache, cache.dex_pc)->SetIsMissingTypes();
       continue;
     }
     for (const ProfileMethodInfo::ProfileClassReference& class_ref : cache.classes) {
@@ -572,12 +585,12 @@
       if (class_dex_data == nullptr) {  // checksum mismatch
         return false;
       }
-      auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(cache.dex_pc);
-      if (dex_pc_data_it->second.is_missing_types) {
+      DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, cache.dex_pc);
+      if (dex_pc_data->is_missing_types) {
         // Don't bother adding classes if we are missing types.
         break;
       }
-      dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index);
+      dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.type_index);
     }
   }
   return true;
@@ -614,13 +627,13 @@
     uint8_t dex_to_classes_map_size;
     READ_UINT(uint16_t, buffer, dex_pc, error);
     READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error);
-    auto dex_pc_data_it = inline_cache->FindOrAdd(dex_pc);
+    DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, dex_pc);
     if (dex_to_classes_map_size == kIsMissingTypesEncoding) {
-      dex_pc_data_it->second.SetIsMissingTypes();
+      dex_pc_data->SetIsMissingTypes();
       continue;
     }
     if (dex_to_classes_map_size == kIsMegamorphicEncoding) {
-      dex_pc_data_it->second.SetIsMegamorphic();
+      dex_pc_data->SetIsMegamorphic();
       continue;
     }
     for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) {
@@ -636,7 +649,7 @@
       for (; dex_classes_size > 0; dex_classes_size--) {
         uint16_t type_index;
         READ_UINT(uint16_t, buffer, type_index, error);
-        dex_pc_data_it->second.AddClass(dex_profile_index, dex::TypeIndex(type_index));
+        dex_pc_data->AddClass(dex_profile_index, dex::TypeIndex(type_index));
       }
     }
   }
@@ -661,8 +674,8 @@
     READ_UINT(uint16_t, buffer, diff_with_last_method_index, error);
     uint16_t method_index = last_method_index + diff_with_last_method_index;
     last_method_index = method_index;
-    auto it = data->method_map.FindOrAdd(method_index);
-    if (!ReadInlineCache(buffer, number_of_dex_files, &(it->second), error)) {
+    InlineCacheMap* inline_cache = data->FindOrAddMethod(method_index);
+    if (!ReadInlineCache(buffer, number_of_dex_files, inline_cache, error)) {
       return false;
     }
   }
@@ -1091,19 +1104,19 @@
     // Merge the methods and the inline caches.
     for (const auto& other_method_it : other_dex_data->method_map) {
       uint16_t other_method_index = other_method_it.first;
-      auto method_it = dex_data->method_map.FindOrAdd(other_method_index);
+      InlineCacheMap* inline_cache = dex_data->FindOrAddMethod(other_method_index);
       const auto& other_inline_cache = other_method_it.second;
       for (const auto& other_ic_it : other_inline_cache) {
         uint16_t other_dex_pc = other_ic_it.first;
         const ClassSet& other_class_set = other_ic_it.second.classes;
-        auto class_set = method_it->second.FindOrAdd(other_dex_pc);
+        DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, other_dex_pc);
         if (other_ic_it.second.is_missing_types) {
-          class_set->second.SetIsMissingTypes();
+          dex_pc_data->SetIsMissingTypes();
         } else if (other_ic_it.second.is_megamorphic) {
-          class_set->second.SetIsMegamorphic();
+          dex_pc_data->SetIsMegamorphic();
         } else {
           for (const auto& class_it : other_class_set) {
-            class_set->second.AddClass(dex_profile_index_remap.Get(
+            dex_pc_data->AddClass(dex_profile_index_remap.Get(
                 class_it.dex_profile_index), class_it.type_index);
           }
         }
@@ -1143,14 +1156,15 @@
   return nullptr;
 }
 
-bool ProfileCompilationInfo::GetMethod(const std::string& dex_location,
-                                       uint32_t dex_checksum,
-                                       uint16_t dex_method_index,
-                                       /*out*/OfflineProfileMethodInfo* pmi) const {
+std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> ProfileCompilationInfo::GetMethod(
+      const std::string& dex_location,
+      uint32_t dex_checksum,
+      uint16_t dex_method_index) const {
   const InlineCacheMap* inline_caches = FindMethod(dex_location, dex_checksum, dex_method_index);
   if (inline_caches == nullptr) {
-    return false;
+    return nullptr;
   }
+  std::unique_ptr<OfflineProfileMethodInfo> pmi(new OfflineProfileMethodInfo(arena_.get()));
 
   pmi->dex_references.resize(info_.size());
   for (const DexFileData* dex_data : info_) {
@@ -1160,7 +1174,7 @@
 
   // TODO(calin): maybe expose a direct pointer to avoid copying
   pmi->inline_caches = *inline_caches;
-  return true;
+  return pmi;
 }
 
 
@@ -1170,7 +1184,7 @@
     if (!ChecksumMatch(dex_file, dex_data->checksum)) {
       return false;
     }
-    const std::set<dex::TypeIndex>& classes = dex_data->class_set;
+    const ArenaSet<dex::TypeIndex>& classes = dex_data->class_set;
     return classes.find(type_idx) != classes.end();
   }
   return false;
@@ -1286,7 +1300,9 @@
     return false;
   }
   *method_map = dex_data->method_map;
-  *class_set = dex_data->class_set;
+  for (const dex::TypeIndex& type_index : dex_data->class_set) {
+    class_set->insert(type_index);
+  }
   return true;
 }
 
@@ -1324,12 +1340,6 @@
   return ret;
 }
 
-void ProfileCompilationInfo::ClearResolvedClasses() {
-  for (DexFileData* dex_data : info_) {
-    dex_data->class_set.clear();
-  }
-}
-
 // Naive implementation to generate a random profile file suitable for testing.
 bool ProfileCompilationInfo::GenerateTestProfile(int fd,
                                                  uint16_t number_of_dex_files,
@@ -1441,17 +1451,21 @@
   return true;
 }
 
-void ProfileCompilationInfo::ClearProfile() {
-  for (DexFileData* dex_data : info_) {
-    delete dex_data;
-  }
-  info_.clear();
-  profile_key_map_.clear();
-}
-
 bool ProfileCompilationInfo::IsEmpty() const {
   DCHECK_EQ(info_.empty(), profile_key_map_.empty());
   return info_.empty();
 }
 
+ProfileCompilationInfo::InlineCacheMap*
+ProfileCompilationInfo::DexFileData::FindOrAddMethod(uint16_t method_index) {
+  return &(method_map.FindOrAdd(
+      method_index,
+      InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)))->second);
+}
+
+ProfileCompilationInfo::DexPcData*
+ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc) {
+  return &(inline_cache->FindOrAdd(dex_pc, DexPcData(arena_.get()))->second);
+}
+
 }  // namespace art
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index ee1935f..2dd91e7 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -17,11 +17,12 @@
 #ifndef ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_
 #define ART_RUNTIME_JIT_PROFILE_COMPILATION_INFO_H_
 
-#include <memory>
 #include <set>
 #include <vector>
 
 #include "atomic.h"
+#include "base/arena_object.h"
+#include "base/arena_containers.h"
 #include "dex_cache_resolved_classes.h"
 #include "dex_file.h"
 #include "dex_file_types.h"
@@ -115,7 +116,7 @@
   // We cannot rely on the actual multidex index because a single profile may store
   // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
   // and one from split-B.
-  struct ClassReference {
+  struct ClassReference : public ValueObject {
     ClassReference(uint8_t dex_profile_idx, const dex::TypeIndex& type_idx) :
       dex_profile_index(dex_profile_idx), type_index(type_idx) {}
 
@@ -133,13 +134,16 @@
   };
 
   // The set of classes that can be found at a given dex pc.
-  using ClassSet = std::set<ClassReference>;
+  using ClassSet = ArenaSet<ClassReference>;
 
   // Encodes the actual inline cache for a given dex pc (whether or not the receiver is
   // megamorphic and its possible types).
   // If the receiver is megamorphic or is missing types the set of classes will be empty.
-  struct DexPcData {
-    DexPcData() : is_missing_types(false), is_megamorphic(false) {}
+  struct DexPcData : public ArenaObject<kArenaAllocProfile> {
+    explicit DexPcData(ArenaAllocator* arena)
+        : is_missing_types(false),
+          is_megamorphic(false),
+          classes(std::less<ClassReference>(), arena->Adapter(kArenaAllocProfile)) {}
     void AddClass(uint16_t dex_profile_idx, const dex::TypeIndex& type_idx);
     void SetIsMegamorphic() {
       if (is_missing_types) return;
@@ -166,16 +170,19 @@
   };
 
   // The inline cache map: DexPc -> DexPcData.
-  using InlineCacheMap = SafeMap<uint16_t, DexPcData>;
+  using InlineCacheMap = ArenaSafeMap<uint16_t, DexPcData>;
 
   // Maps a method dex index to its inline cache.
-  using MethodMap = SafeMap<uint16_t, InlineCacheMap>;
+  using MethodMap = ArenaSafeMap<uint16_t, InlineCacheMap>;
 
   // Encodes the full set of inline caches for a given method.
   // The dex_references vector is indexed according to the ClassReference::dex_profile_index.
   // 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)) {}
+
     bool operator==(const OfflineProfileMethodInfo& other) const;
 
     std::vector<DexReference> dex_references;
@@ -183,9 +190,9 @@
   };
 
   // Public methods to create, extend or query the profile.
+  ProfileCompilationInfo();
+  explicit ProfileCompilationInfo(ArenaPool* arena_pool);
 
-  ProfileCompilationInfo() {}
-  ProfileCompilationInfo(const ProfileCompilationInfo& pci);
   ~ProfileCompilationInfo();
 
   // Add the given methods and classes to the current profile object.
@@ -223,12 +230,12 @@
   // Return true if the class's type is present in the profiling info.
   bool ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const;
 
-  // Return true if the method is present in the profiling info.
-  // If the method is found, `pmi` is populated with its inline caches.
-  bool GetMethod(const std::string& dex_location,
-                 uint32_t dex_checksum,
-                 uint16_t dex_method_index,
-                 /*out*/OfflineProfileMethodInfo* pmi) const;
+  // 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.
+  std::unique_ptr<OfflineProfileMethodInfo> GetMethod(const std::string& dex_location,
+                                                      uint32_t dex_checksum,
+                                                      uint16_t dex_method_index) const;
 
   // Dump all the loaded profile info into a string and returns it.
   // If dex_files is not null then the method indices will be resolved to their
@@ -253,9 +260,6 @@
   std::set<DexCacheResolvedClasses> GetResolvedClasses(
       const std::unordered_set<std::string>& dex_files_locations) const;
 
-  // Clear the resolved classes from the current object.
-  void ClearResolvedClasses();
-
   // Return the profile key associated with the given dex location.
   static std::string GetProfileDexFileKey(const std::string& dex_location);
 
@@ -277,6 +281,8 @@
   static bool Equals(const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi1,
                      const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi2);
 
+  ArenaAllocator* GetArena() { return arena_.get(); }
+
  private:
   enum ProfileLoadSatus {
     kProfileLoadWouldOverwiteData,
@@ -295,9 +301,20 @@
   // profile) fields in this struct because we can infer them from
   // profile_key_map_ and info_. However, it makes the profiles logic much
   // simpler if we have references here as well.
-  struct DexFileData {
-    DexFileData(const std::string& key, uint32_t location_checksum, uint16_t index)
-         : profile_key(key), profile_index(index), checksum(location_checksum) {}
+  struct DexFileData : public ArenaObject<kArenaAllocProfile> {
+    DexFileData(ArenaAllocator* arena,
+                const std::string& key,
+                uint32_t location_checksum,
+                uint16_t index)
+        : arena_(arena),
+          profile_key(key),
+          profile_index(index),
+          checksum(location_checksum),
+          method_map(std::less<uint16_t>(), arena->Adapter(kArenaAllocProfile)),
+          class_set(std::less<dex::TypeIndex>(), arena->Adapter(kArenaAllocProfile)) {}
+
+    // The arena used to allocate new inline cache maps.
+    ArenaAllocator* arena_;
     // The profile key this data belongs to.
     std::string profile_key;
     // The profile index of this dex file (matches ClassReference#dex_profile_index).
@@ -308,11 +325,15 @@
     MethodMap method_map;
     // The classes which have been profiled. Note that these don't necessarily include
     // all the classes that can be found in the inline caches reference.
-    std::set<dex::TypeIndex> class_set;
+    ArenaSet<dex::TypeIndex> class_set;
 
     bool operator==(const DexFileData& other) const {
       return checksum == other.checksum && method_map == other.method_map;
     }
+
+    // Find the inline caches of the the given method index. Add an empty entry if
+    // no previous data is found.
+    InlineCacheMap* FindOrAddMethod(uint16_t method_index);
   };
 
   // Return the profile data for the given profile key or null if the dex location
@@ -352,9 +373,6 @@
   // doesn't contain the key.
   const DexFileData* FindDexData(const std::string& profile_key) const;
 
-  // Clear all the profile data.
-  void ClearProfile();
-
   // Checks if the profile is empty.
   bool IsEmpty() const;
 
@@ -485,20 +503,27 @@
       const ClassSet& classes,
       /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map);
 
+  // Find the data for the dex_pc in the inline cache. Adds an empty entry
+  // if no previous data exists.
+  DexPcData* FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc);
+
   friend class ProfileCompilationInfoTest;
   friend class CompilerDriverProfileTest;
   friend class ProfileAssistantTest;
   friend class Dex2oatLayoutTest;
 
+  std::unique_ptr<ArenaPool> default_arena_pool_;
+  std::unique_ptr<ArenaAllocator> arena_;
+
   // Vector containing the actual profile info.
   // The vector index is the profile index of the dex data and
   // matched DexFileData::profile_index.
-  std::vector<DexFileData*> info_;
+  ArenaVector<DexFileData*> info_;
 
   // Cache mapping profile keys to profile index.
   // This is used to speed up searches since it avoids iterating
   // over the info_ vector when searching by profile key.
-  SafeMap<const std::string, uint8_t> profile_key_map_;
+  ArenaSafeMap<const std::string, uint8_t> profile_key_map_;
 };
 
 }  // namespace art
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index e8f4ce2..a054199 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -25,12 +25,18 @@
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "handle_scope-inl.h"
+#include "linear_alloc.h"
 #include "jit/profile_compilation_info.h"
 #include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
 class ProfileCompilationInfoTest : public CommonRuntimeTest {
+ public:
+  virtual void PostRuntimeCreate() {
+    arena_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
+  }
+
  protected:
   std::vector<ArtMethod*> GetVirtualMethods(jobject class_loader,
                                             const std::string& clazz) {
@@ -158,11 +164,12 @@
 
   ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo(
         const ProfileMethodInfo& pmi) {
-    ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi;
+    ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(arena_.get());
     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(inline_cache.dex_pc)->second;
+          offline_pmi.inline_caches.FindOrAdd(
+              inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(arena_.get()))->second;
       if (inline_cache.is_missing_types) {
         dex_pc_data.SetIsMissingTypes();
       }
@@ -184,7 +191,7 @@
 
   // Creates an offline profile used for testing inline caches.
   ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() {
-    ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+    ProfileCompilationInfo::OfflineProfileMethodInfo pmi(arena_.get());
 
     pmi.dex_references.emplace_back("dex_location1", /* checksum */1);
     pmi.dex_references.emplace_back("dex_location2", /* checksum */2);
@@ -192,13 +199,13 @@
 
     // Monomorphic
     for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
-      ProfileCompilationInfo::DexPcData dex_pc_data;
+      ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.AddClass(0, dex::TypeIndex(0));
       pmi.inline_caches.Put(dex_pc, dex_pc_data);
     }
     // Polymorphic
     for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
-      ProfileCompilationInfo::DexPcData dex_pc_data;
+      ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.AddClass(0, dex::TypeIndex(0));
       dex_pc_data.AddClass(1, dex::TypeIndex(1));
       dex_pc_data.AddClass(2, dex::TypeIndex(2));
@@ -207,13 +214,13 @@
     }
     // Megamorphic
     for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
-      ProfileCompilationInfo::DexPcData dex_pc_data;
+      ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.SetIsMegamorphic();
       pmi.inline_caches.Put(dex_pc, dex_pc_data);
     }
     // Missing types
     for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
-      ProfileCompilationInfo::DexPcData dex_pc_data;
+      ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
       dex_pc_data.SetIsMissingTypes();
       pmi.inline_caches.Put(dex_pc, dex_pc_data);
     }
@@ -239,6 +246,8 @@
   // They should not change anyway.
   static constexpr int kProfileMagicSize = 4;
   static constexpr int kProfileVersionSize = 4;
+
+  std::unique_ptr<ArenaAllocator> arena_;
 };
 
 TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
@@ -500,18 +509,14 @@
 
   ASSERT_TRUE(loaded_info.Equals(saved_info));
 
-  ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1;
-  ASSERT_TRUE(loaded_info.GetMethod("dex_location1",
-                                    /* checksum */ 1,
-                                    /* method_idx */ 3,
-                                    &loaded_pmi1));
-  ASSERT_TRUE(loaded_pmi1 == pmi);
-  ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi2;
-  ASSERT_TRUE(loaded_info.GetMethod("dex_location4",
-                                    /* checksum */ 4,
-                                    /* method_idx */ 3,
-                                    &loaded_pmi2));
-  ASSERT_TRUE(loaded_pmi2 == pmi);
+  std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
+      loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
+  ASSERT_TRUE(loaded_pmi1 != nullptr);
+  ASSERT_TRUE(*loaded_pmi1 == pmi);
+  std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 =
+      loaded_info.GetMethod("dex_location4", /* checksum */ 4, /* method_idx */ 3);
+  ASSERT_TRUE(loaded_pmi2 != nullptr);
+  ASSERT_TRUE(*loaded_pmi2 == pmi);
 }
 
 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) {
@@ -550,12 +555,11 @@
 
   ASSERT_TRUE(loaded_info.Equals(saved_info));
 
-  ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1;
-  ASSERT_TRUE(loaded_info.GetMethod("dex_location1",
-                                    /* checksum */ 1,
-                                    /* method_idx */ 3,
-                                    &loaded_pmi1));
-  ASSERT_TRUE(loaded_pmi1 == pmi_extra);
+  std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
+      loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
+
+  ASSERT_TRUE(loaded_pmi1 != nullptr);
+  ASSERT_TRUE(*loaded_pmi1 == pmi_extra);
 }
 
 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCaches) {
@@ -602,12 +606,10 @@
 
   ASSERT_TRUE(loaded_info.Equals(saved_info));
 
-  ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1;
-  ASSERT_TRUE(loaded_info.GetMethod("dex_location1",
-                                    /* checksum */ 1,
-                                    /* method_idx */ 3,
-                                    &loaded_pmi1));
-  ASSERT_TRUE(loaded_pmi1 == pmi_extra);
+  std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
+      loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
+  ASSERT_TRUE(loaded_pmi1 != nullptr);
+  ASSERT_TRUE(*loaded_pmi1 == pmi_extra);
 }
 
 TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
@@ -638,14 +640,14 @@
     for (ArtMethod* m : main_methods) {
       ASSERT_TRUE(info.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
       const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
-      ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi;
-      ASSERT_TRUE(info.GetMethod(m->GetDexFile()->GetLocation(),
-                                 m->GetDexFile()->GetLocationChecksum(),
-                                 m->GetDexMethodIndex(),
-                                 &offline_pmi));
+      std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi =
+          info.GetMethod(m->GetDexFile()->GetLocation(),
+                         m->GetDexFile()->GetLocationChecksum(),
+                         m->GetDexMethodIndex());
+      ASSERT_TRUE(offline_pmi != nullptr);
       ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi =
           ConvertProfileMethodInfo(pmi);
-      ASSERT_EQ(converted_pmi, offline_pmi);
+      ASSERT_EQ(converted_pmi, *offline_pmi);
     }
   }
 }
@@ -671,21 +673,21 @@
   ProfileCompilationInfo info;
   ProfileCompilationInfo info_reindexed;
 
-  ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+  ProfileCompilationInfo::OfflineProfileMethodInfo pmi(arena_.get());
   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;
+    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);
   }
 
-  ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed;
+  ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed(arena_.get());
   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;
+    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);
@@ -705,23 +707,20 @@
       "dex_location1", /* checksum */ 1, method_idx, pmi_reindexed, &info_reindexed));
   }
 
-  ProfileCompilationInfo info_backup = info;
+  ProfileCompilationInfo info_backup;
+  info_backup.MergeWith(info);
   ASSERT_TRUE(info.MergeWith(info_reindexed));
   // Merging should have no effect as we're adding the exact same stuff.
   ASSERT_TRUE(info.Equals(info_backup));
   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
-    ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1;
-    ASSERT_TRUE(info.GetMethod("dex_location1",
-                                      /* checksum */ 1,
-                                      /* method_idx */ method_idx,
-                                      &loaded_pmi1));
-    ASSERT_TRUE(loaded_pmi1 == pmi);
-    ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi2;
-    ASSERT_TRUE(info.GetMethod("dex_location2",
-                                      /* checksum */ 2,
-                                      /* method_idx */ method_idx,
-                                      &loaded_pmi2));
-    ASSERT_TRUE(loaded_pmi2 == pmi);
+    std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
+        info.GetMethod("dex_location1", /* checksum */ 1, method_idx);
+    ASSERT_TRUE(loaded_pmi1 != nullptr);
+    ASSERT_TRUE(*loaded_pmi1 == pmi);
+    std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 =
+        info.GetMethod("dex_location2", /* checksum */ 2, method_idx);
+    ASSERT_TRUE(loaded_pmi2 != nullptr);
+    ASSERT_TRUE(*loaded_pmi2 == pmi);
   }
 }
 
@@ -739,9 +738,9 @@
 
 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) {
   // Create a megamorphic inline cache.
-  ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+  ProfileCompilationInfo::OfflineProfileMethodInfo pmi(arena_.get());
   pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
-  ProfileCompilationInfo::DexPcData dex_pc_data;
+  ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
   dex_pc_data.SetIsMegamorphic();
   pmi.inline_caches.Put(/*dex_pc*/ 0, dex_pc_data);
 
@@ -768,9 +767,9 @@
 
 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCachesMerge) {
   // Create an inline cache with missing types
-  ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+  ProfileCompilationInfo::OfflineProfileMethodInfo pmi(arena_.get());
   pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
-  ProfileCompilationInfo::DexPcData dex_pc_data;
+  ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
   dex_pc_data.SetIsMissingTypes();
   pmi.inline_caches.Put(/*dex_pc*/ 0, dex_pc_data);
 
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 2dba9b7..0c94a94 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -64,6 +64,12 @@
   AddTrackedLocations(output_filename, code_paths);
 }
 
+ProfileSaver::~ProfileSaver() {
+  for (auto& it : profile_cache_) {
+    delete it.second;
+  }
+}
+
 void ProfileSaver::Run() {
   Thread* self = Thread::Current();
 
@@ -253,9 +259,9 @@
                        << " (" << classes.GetDexLocation() << ")";
       }
     }
-    auto info_it = profile_cache_.Put(filename, ProfileCompilationInfo());
+    auto info_it = profile_cache_.Put(filename, new ProfileCompilationInfo(Runtime::Current()->GetArenaPool()));
 
-    ProfileCompilationInfo* cached_info = &(info_it->second);
+    ProfileCompilationInfo* cached_info = info_it->second;
     cached_info->AddMethodsAndClasses(profile_methods_for_location,
                                       resolved_classes_for_location);
     total_number_of_profile_entries_cached += resolved_classes_for_location.size();
@@ -279,7 +285,6 @@
   }
 
   bool profile_file_saved = false;
-  uint64_t total_number_of_profile_entries_cached = 0;
   if (number_of_new_methods != nullptr) {
     *number_of_new_methods = 0;
   }
@@ -300,60 +305,68 @@
       jit_code_cache_->GetProfiledMethods(locations, profile_methods);
       total_number_of_code_cache_queries_++;
     }
-    ProfileCompilationInfo info;
-    if (!info.Load(filename, /*clear_if_invalid*/ true)) {
-      LOG(WARNING) << "Could not forcefully load profile " << filename;
-      continue;
-    }
-    uint64_t last_save_number_of_methods = info.GetNumberOfMethods();
-    uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses();
+    {
+      ProfileCompilationInfo info(Runtime::Current()->GetArenaPool());
+      if (!info.Load(filename, /*clear_if_invalid*/ true)) {
+        LOG(WARNING) << "Could not forcefully load profile " << filename;
+        continue;
+      }
+      uint64_t last_save_number_of_methods = info.GetNumberOfMethods();
+      uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses();
 
-    info.AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>());
-    auto profile_cache_it = profile_cache_.find(filename);
-    if (profile_cache_it != profile_cache_.end()) {
-      info.MergeWith(profile_cache_it->second);
-    }
-
-    int64_t delta_number_of_methods = info.GetNumberOfMethods() - last_save_number_of_methods;
-    int64_t delta_number_of_classes = info.GetNumberOfResolvedClasses() - last_save_number_of_classes;
-
-    if (!force_save &&
-        delta_number_of_methods < options_.GetMinMethodsToSave() &&
-        delta_number_of_classes < options_.GetMinClassesToSave()) {
-      VLOG(profiler) << "Not enough information to save to: " << filename
-          << " Number of methods: " << delta_number_of_methods
-          << " Number of classes: " << delta_number_of_classes;
-      total_number_of_skipped_writes_++;
-      continue;
-    }
-    if (number_of_new_methods != nullptr) {
-      *number_of_new_methods = std::max(static_cast<uint16_t>(delta_number_of_methods),
-                                        *number_of_new_methods);
-    }
-    uint64_t bytes_written;
-    // Force the save. In case the profile data is corrupted or the the profile
-    // has the wrong version this will "fix" the file to the correct format.
-    if (info.Save(filename, &bytes_written)) {
-      // We managed to save the profile. Clear the cache stored during startup.
+      info.AddMethodsAndClasses(profile_methods,
+                                std::set<DexCacheResolvedClasses>());
+      auto profile_cache_it = profile_cache_.find(filename);
       if (profile_cache_it != profile_cache_.end()) {
-        profile_cache_.erase(profile_cache_it);
-        total_number_of_profile_entries_cached = 0;
+        info.MergeWith(*(profile_cache_it->second));
       }
-      if (bytes_written > 0) {
-        total_number_of_writes_++;
-        total_bytes_written_ += bytes_written;
-        profile_file_saved = true;
-      } else {
-        // At this point we could still have avoided the write.
-        // We load and merge the data from the file lazily at its first ever
-        // save attempt. So, whatever we are trying to save could already be
-        // in the file.
+
+      int64_t delta_number_of_methods =
+          info.GetNumberOfMethods() - last_save_number_of_methods;
+      int64_t delta_number_of_classes =
+          info.GetNumberOfResolvedClasses() - last_save_number_of_classes;
+
+      if (!force_save &&
+          delta_number_of_methods < options_.GetMinMethodsToSave() &&
+          delta_number_of_classes < options_.GetMinClassesToSave()) {
+        VLOG(profiler) << "Not enough information to save to: " << filename
+                       << " Number of methods: " << delta_number_of_methods
+                       << " Number of classes: " << delta_number_of_classes;
         total_number_of_skipped_writes_++;
+        continue;
       }
-    } else {
-      LOG(WARNING) << "Could not save profiling info to " << filename;
-      total_number_of_failed_writes_++;
+      if (number_of_new_methods != nullptr) {
+        *number_of_new_methods =
+            std::max(static_cast<uint16_t>(delta_number_of_methods),
+                     *number_of_new_methods);
+      }
+      uint64_t bytes_written;
+      // Force the save. In case the profile data is corrupted or the the profile
+      // has the wrong version this will "fix" the file to the correct format.
+      if (info.Save(filename, &bytes_written)) {
+        // We managed to save the profile. Clear the cache stored during startup.
+        if (profile_cache_it != profile_cache_.end()) {
+          ProfileCompilationInfo *cached_info = profile_cache_it->second;
+          profile_cache_.erase(profile_cache_it);
+          delete cached_info;
+        }
+        if (bytes_written > 0) {
+          total_number_of_writes_++;
+          total_bytes_written_ += bytes_written;
+          profile_file_saved = true;
+        } else {
+          // At this point we could still have avoided the write.
+          // We load and merge the data from the file lazily at its first ever
+          // save attempt. So, whatever we are trying to save could already be
+          // in the file.
+          total_number_of_skipped_writes_++;
+        }
+      } else {
+        LOG(WARNING) << "Could not save profiling info to " << filename;
+        total_number_of_failed_writes_++;
+      }
     }
+    Runtime::Current()->GetArenaPool()->TrimMaps();
   }
 
   return profile_file_saved;
@@ -579,7 +592,7 @@
                                  uint16_t method_idx) {
   MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
   if (instance_ != nullptr) {
-    ProfileCompilationInfo info;
+    ProfileCompilationInfo info(Runtime::Current()->GetArenaPool());
     if (!info.Load(profile, /*clear_if_invalid*/false)) {
       return false;
     }
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 60c9cc6..01d72fe 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -65,6 +65,7 @@
                const std::string& output_filename,
                jit::JitCodeCache* jit_code_cache,
                const std::vector<std::string>& code_paths);
+  ~ProfileSaver();
 
   // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
   static void* RunProfileSaverThread(void* arg)
@@ -131,7 +132,7 @@
   // we don't hammer the disk to save them right away.
   // The size of this cache is usually very small and tops
   // to just a few hundreds entries in the ProfileCompilationInfo objects.
-  SafeMap<std::string, ProfileCompilationInfo> profile_cache_;
+  SafeMap<std::string, ProfileCompilationInfo*> profile_cache_;
 
   // Save period condition support.
   Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;