ART: Swap-space in the compiler

Introduce a swap-space and corresponding allocator to transparently
switch native allocations to memory backed by a file.

Bug: 18596910
Change-Id: I131448f3907115054a592af73db86d2b9257ea33
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 6124c7b..71d504c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -153,6 +153,7 @@
   compiler/output_stream_test.cc \
   compiler/utils/arena_allocator_test.cc \
   compiler/utils/dedupe_set_test.cc \
+  compiler/utils/swap_space_test.cc \
   compiler/utils/arm/managed_register_arm_test.cc \
   compiler/utils/arm64/managed_register_arm64_test.cc \
   compiler/utils/x86/managed_register_x86_test.cc \
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 69f9387..b0ec8fb 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -114,6 +114,7 @@
 	utils/x86_64/assembler_x86_64.cc \
 	utils/x86_64/managed_register_x86_64.cc \
 	utils/scoped_arena_allocator.cc \
+	utils/swap_space.cc \
 	buffered_output_stream.cc \
 	compilers.cc \
 	compiler.cc \
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 00245c4..7617247 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -162,18 +162,18 @@
                                                             method->GetDexMethodIndex()));
   }
   if (compiled_method != nullptr) {
-    const std::vector<uint8_t>* code = compiled_method->GetQuickCode();
+    const SwapVector<uint8_t>* code = compiled_method->GetQuickCode();
     const void* code_ptr;
     if (code != nullptr) {
       uint32_t code_size = code->size();
       CHECK_NE(0u, code_size);
-      const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
+      const SwapVector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
       uint32_t vmap_table_offset = vmap_table.empty() ? 0u
           : sizeof(OatQuickMethodHeader) + vmap_table.size();
-      const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
+      const SwapVector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
       uint32_t mapping_table_offset = mapping_table.empty() ? 0u
           : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size();
-      const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
+      const SwapVector<uint8_t>& gc_map = compiled_method->GetGcMap();
       uint32_t gc_map_offset = gc_map.empty() ? 0u
           : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size() + gc_map.size();
       OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, gc_map_offset,
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index f098a34..40ef513 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -20,7 +20,7 @@
 namespace art {
 
 CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
-                           const std::vector<uint8_t>& quick_code)
+                           const ArrayRef<const uint8_t>& quick_code)
     : compiler_driver_(compiler_driver), instruction_set_(instruction_set),
       portable_code_(nullptr), quick_code_(nullptr) {
   SetCode(&quick_code, nullptr);
@@ -29,22 +29,20 @@
 CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
                            const std::string& elf_object, const std::string& symbol)
     : compiler_driver_(compiler_driver), instruction_set_(instruction_set),
-      portable_code_(nullptr), quick_code_(nullptr), symbol_(symbol) {
+      portable_code_(compiler_driver_->DeduplicateCode(
+          ArrayRef<const uint8_t>(reinterpret_cast<const uint8_t*>(elf_object.data()),
+                                  elf_object.size()))),
+      quick_code_(nullptr), symbol_(symbol) {
   CHECK_NE(elf_object.size(), 0U);
   CHECK_NE(symbol.size(), 0U);
-  std::vector<uint8_t> temp_code(elf_object.size());
-  for (size_t i = 0; i < elf_object.size(); ++i) {
-    temp_code[i] = elf_object[i];
-  }
   // TODO: we shouldn't just shove ELF objects in as "code" but
   // change to have different kinds of compiled methods.  This is
   // being deferred until we work on hybrid execution or at least
   // until we work on batch compilation.
-  SetCode(nullptr, &temp_code);
 }
 
-void CompiledCode::SetCode(const std::vector<uint8_t>* quick_code,
-                           const std::vector<uint8_t>* portable_code) {
+void CompiledCode::SetCode(const ArrayRef<const uint8_t>* quick_code,
+                           const ArrayRef<const uint8_t>* portable_code) {
   if (portable_code != nullptr) {
     CHECK(!portable_code->empty());
     portable_code_ = compiler_driver_->DeduplicateCode(*portable_code);
@@ -144,56 +142,93 @@
 
 CompiledMethod::CompiledMethod(CompilerDriver* driver,
                                InstructionSet instruction_set,
-                               const std::vector<uint8_t>& quick_code,
+                               const ArrayRef<const uint8_t>& quick_code,
                                const size_t frame_size_in_bytes,
                                const uint32_t core_spill_mask,
                                const uint32_t fp_spill_mask,
-                               const std::vector<uint8_t>& mapping_table,
-                               const std::vector<uint8_t>& vmap_table,
-                               const std::vector<uint8_t>& native_gc_map,
-                               const std::vector<uint8_t>* cfi_info)
+                               const ArrayRef<const uint8_t>& mapping_table,
+                               const ArrayRef<const uint8_t>& vmap_table,
+                               const ArrayRef<const uint8_t>& native_gc_map,
+                               const ArrayRef<const uint8_t>& cfi_info)
     : CompiledCode(driver, instruction_set, quick_code), frame_size_in_bytes_(frame_size_in_bytes),
       core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
-  mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
-  vmap_table_(driver->DeduplicateVMapTable(vmap_table)),
-  gc_map_(driver->DeduplicateGCMap(native_gc_map)),
-  cfi_info_(driver->DeduplicateCFIInfo(cfi_info)) {
+      mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
+      vmap_table_(driver->DeduplicateVMapTable(vmap_table)),
+      gc_map_(driver->DeduplicateGCMap(native_gc_map)),
+      cfi_info_(cfi_info.data() == nullptr ? nullptr : driver->DeduplicateCFIInfo(cfi_info)) {
 }
 
 CompiledMethod::CompiledMethod(CompilerDriver* driver,
                                InstructionSet instruction_set,
-                               const std::vector<uint8_t>& code,
+                               const ArrayRef<const uint8_t>& code,
                                const size_t frame_size_in_bytes,
                                const uint32_t core_spill_mask,
                                const uint32_t fp_spill_mask)
     : CompiledCode(driver, instruction_set, code),
       frame_size_in_bytes_(frame_size_in_bytes),
       core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
-      mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
-      vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
-      gc_map_(driver->DeduplicateGCMap(std::vector<uint8_t>())),
       cfi_info_(nullptr) {
+  mapping_table_ = driver->DeduplicateMappingTable(ArrayRef<const uint8_t>());
+  vmap_table_ = driver->DeduplicateVMapTable(ArrayRef<const uint8_t>());
+  gc_map_ = driver->DeduplicateGCMap(ArrayRef<const uint8_t>());
 }
 
 // Constructs a CompiledMethod for the Portable compiler.
 CompiledMethod::CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set,
-                               const std::string& code, const std::vector<uint8_t>& gc_map,
+                               const std::string& code, const ArrayRef<const uint8_t>& gc_map,
                                const std::string& symbol)
     : CompiledCode(driver, instruction_set, code, symbol),
       frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
-      fp_spill_mask_(0), gc_map_(driver->DeduplicateGCMap(gc_map)) {
-  mapping_table_ = driver->DeduplicateMappingTable(std::vector<uint8_t>());
-  vmap_table_ = driver->DeduplicateVMapTable(std::vector<uint8_t>());
+      fp_spill_mask_(0), gc_map_(driver->DeduplicateGCMap(gc_map)),
+      cfi_info_(nullptr) {
+  mapping_table_ = driver->DeduplicateMappingTable(ArrayRef<const uint8_t>());
+  vmap_table_ = driver->DeduplicateVMapTable(ArrayRef<const uint8_t>());
 }
 
 CompiledMethod::CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set,
                                const std::string& code, const std::string& symbol)
     : CompiledCode(driver, instruction_set, code, symbol),
       frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
-      fp_spill_mask_(0) {
-  mapping_table_ = driver->DeduplicateMappingTable(std::vector<uint8_t>());
-  vmap_table_ = driver->DeduplicateVMapTable(std::vector<uint8_t>());
-  gc_map_ = driver->DeduplicateGCMap(std::vector<uint8_t>());
+      fp_spill_mask_(0), cfi_info_(nullptr) {
+  mapping_table_ = driver->DeduplicateMappingTable(ArrayRef<const uint8_t>());
+  vmap_table_ = driver->DeduplicateVMapTable(ArrayRef<const uint8_t>());
+  gc_map_ = driver->DeduplicateGCMap(ArrayRef<const uint8_t>());
+}
+
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(CompilerDriver* driver,
+    InstructionSet instruction_set,
+    const ArrayRef<const uint8_t>& quick_code,
+    const size_t frame_size_in_bytes,
+    const uint32_t core_spill_mask,
+    const uint32_t fp_spill_mask,
+    const ArrayRef<const uint8_t>& mapping_table,
+    const ArrayRef<const uint8_t>& vmap_table,
+    const ArrayRef<const uint8_t>& native_gc_map,
+    const ArrayRef<const uint8_t>& cfi_info) {
+  SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+  CompiledMethod* ret = alloc.allocate(1);
+  alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
+                  fp_spill_mask, mapping_table, vmap_table, native_gc_map, cfi_info);
+  return ret;
+}
+
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(CompilerDriver* driver,
+                                                        InstructionSet instruction_set,
+                                                        const ArrayRef<const uint8_t>& quick_code,
+                                                        const size_t frame_size_in_bytes,
+                                                        const uint32_t core_spill_mask,
+                                                        const uint32_t fp_spill_mask) {
+  SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+  CompiledMethod* ret = alloc.allocate(1);
+  alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
+                  fp_spill_mask);
+  return ret;
+}
+
+void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m) {
+  SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+  alloc.destroy(m);
+  alloc.deallocate(m, 1);
 }
 
 }  // namespace art
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index b8cd851..2d95851 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -23,6 +23,8 @@
 
 #include "instruction_set.h"
 #include "utils.h"
+#include "utils/array_ref.h"
+#include "utils/swap_space.h"
 
 namespace llvm {
   class Function;
@@ -36,7 +38,7 @@
  public:
   // For Quick to supply an code blob
   CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
-               const std::vector<uint8_t>& quick_code);
+               const ArrayRef<const uint8_t>& quick_code);
 
   // For Portable to supply an ELF object
   CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
@@ -46,15 +48,16 @@
     return instruction_set_;
   }
 
-  const std::vector<uint8_t>* GetPortableCode() const {
+  const SwapVector<uint8_t>* GetPortableCode() const {
     return portable_code_;
   }
 
-  const std::vector<uint8_t>* GetQuickCode() const {
+  const SwapVector<uint8_t>* GetQuickCode() const {
     return quick_code_;
   }
 
-  void SetCode(const std::vector<uint8_t>* quick_code, const std::vector<uint8_t>* portable_code);
+  void SetCode(const ArrayRef<const uint8_t>* quick_code,
+               const ArrayRef<const uint8_t>* portable_code);
 
   bool operator==(const CompiledCode& rhs) const;
 
@@ -85,10 +88,10 @@
   const InstructionSet instruction_set_;
 
   // The ELF image for portable.
-  std::vector<uint8_t>* portable_code_;
+  SwapVector<uint8_t>* portable_code_;
 
   // Used to store the PIC code for Quick.
-  std::vector<uint8_t>* quick_code_;
+  SwapVector<uint8_t>* quick_code_;
 
   // Used for the Portable ELF symbol name.
   const std::string symbol_;
@@ -105,26 +108,26 @@
   // Constructs a CompiledMethod for the non-LLVM compilers.
   CompiledMethod(CompilerDriver* driver,
                  InstructionSet instruction_set,
-                 const std::vector<uint8_t>& quick_code,
+                 const ArrayRef<const uint8_t>& quick_code,
                  const size_t frame_size_in_bytes,
                  const uint32_t core_spill_mask,
                  const uint32_t fp_spill_mask,
-                 const std::vector<uint8_t>& mapping_table,
-                 const std::vector<uint8_t>& vmap_table,
-                 const std::vector<uint8_t>& native_gc_map,
-                 const std::vector<uint8_t>* cfi_info);
+                 const ArrayRef<const uint8_t>& mapping_table,
+                 const ArrayRef<const uint8_t>& vmap_table,
+                 const ArrayRef<const uint8_t>& native_gc_map,
+                 const ArrayRef<const uint8_t>& cfi_info);
 
   // Constructs a CompiledMethod for the QuickJniCompiler.
   CompiledMethod(CompilerDriver* driver,
                  InstructionSet instruction_set,
-                 const std::vector<uint8_t>& quick_code,
+                 const ArrayRef<const uint8_t>& quick_code,
                  const size_t frame_size_in_bytes,
                  const uint32_t core_spill_mask,
                  const uint32_t fp_spill_mask);
 
   // Constructs a CompiledMethod for the Portable compiler.
   CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set, const std::string& code,
-                 const std::vector<uint8_t>& gc_map, const std::string& symbol);
+                 const ArrayRef<const uint8_t>& gc_map, const std::string& symbol);
 
   // Constructs a CompiledMethod for the Portable JniCompiler.
   CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set, const std::string& code,
@@ -132,6 +135,26 @@
 
   ~CompiledMethod() {}
 
+  static CompiledMethod* SwapAllocCompiledMethod(CompilerDriver* driver,
+                                                 InstructionSet instruction_set,
+                                                 const ArrayRef<const uint8_t>& quick_code,
+                                                 const size_t frame_size_in_bytes,
+                                                 const uint32_t core_spill_mask,
+                                                 const uint32_t fp_spill_mask,
+                                                 const ArrayRef<const uint8_t>& mapping_table,
+                                                 const ArrayRef<const uint8_t>& vmap_table,
+                                                 const ArrayRef<const uint8_t>& native_gc_map,
+                                                 const ArrayRef<const uint8_t>& cfi_info);
+
+  static CompiledMethod* SwapAllocCompiledMethod(CompilerDriver* driver,
+                                                 InstructionSet instruction_set,
+                                                 const ArrayRef<const uint8_t>& quick_code,
+                                                 const size_t frame_size_in_bytes,
+                                                 const uint32_t core_spill_mask,
+                                                 const uint32_t fp_spill_mask);
+
+  static void ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m);
+
   size_t GetFrameSizeInBytes() const {
     return frame_size_in_bytes_;
   }
@@ -144,22 +167,22 @@
     return fp_spill_mask_;
   }
 
-  const std::vector<uint8_t>& GetMappingTable() const {
+  const SwapVector<uint8_t>& GetMappingTable() const {
     DCHECK(mapping_table_ != nullptr);
     return *mapping_table_;
   }
 
-  const std::vector<uint8_t>& GetVmapTable() const {
+  const SwapVector<uint8_t>& GetVmapTable() const {
     DCHECK(vmap_table_ != nullptr);
     return *vmap_table_;
   }
 
-  const std::vector<uint8_t>& GetGcMap() const {
+  const SwapVector<uint8_t>& GetGcMap() const {
     DCHECK(gc_map_ != nullptr);
     return *gc_map_;
   }
 
-  const std::vector<uint8_t>* GetCFIInfo() const {
+  const SwapVector<uint8_t>* GetCFIInfo() const {
     return cfi_info_;
   }
 
@@ -172,14 +195,14 @@
   const uint32_t fp_spill_mask_;
   // For quick code, a uleb128 encoded map from native PC offset to dex PC aswell as dex PC to
   // native PC offset. Size prefixed.
-  std::vector<uint8_t>* mapping_table_;
+  SwapVector<uint8_t>* mapping_table_;
   // For quick code, a uleb128 encoded map from GPR/FPR register to dex register. Size prefixed.
-  std::vector<uint8_t>* vmap_table_;
+  SwapVector<uint8_t>* vmap_table_;
   // For quick code, a map keyed by native PC indices to bitmaps describing what dalvik registers
   // are live. For portable code, the key is a dalvik PC.
-  std::vector<uint8_t>* gc_map_;
+  SwapVector<uint8_t>* gc_map_;
   // For quick code, a FDE entry for the debug_frame section.
-  std::vector<uint8_t>* cfi_info_;
+  SwapVector<uint8_t>* cfi_info_;
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 5919f37..cb5fbb3 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -1098,11 +1098,18 @@
   }
 
   std::unique_ptr<std::vector<uint8_t>> cfi_info(ReturnCallFrameInformation());
-  CompiledMethod* result =
-      new CompiledMethod(cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_,
-                         core_spill_mask_, fp_spill_mask_, encoded_mapping_table_,
-                         vmap_encoder.GetData(), native_gc_map_, cfi_info.get());
-  return result;
+  ArrayRef<const uint8_t> cfi_ref;
+  if (cfi_info.get() != nullptr) {
+    cfi_ref = ArrayRef<const uint8_t>(*cfi_info);
+  }
+  return CompiledMethod::SwapAllocCompiledMethod(
+      cu_->compiler_driver, cu_->instruction_set,
+      ArrayRef<const uint8_t>(code_buffer_),
+      frame_size_, core_spill_mask_, fp_spill_mask_,
+      ArrayRef<const uint8_t>(encoded_mapping_table_),
+      ArrayRef<const uint8_t>(vmap_encoder.GetData()),
+      ArrayRef<const uint8_t>(native_gc_map_),
+      cfi_ref);
 }
 
 size_t Mir2Lir::GetMaxPossibleCompilerTemps() const {
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index f6db1fc..6f6eeec 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -60,6 +60,7 @@
 #include "thread_pool.h"
 #include "trampolines/trampoline_compiler.h"
 #include "transaction.h"
+#include "utils/swap_space.h"
 #include "verifier/method_verifier.h"
 #include "verifier/method_verifier-inl.h"
 
@@ -337,8 +338,10 @@
                                bool image, std::set<std::string>* image_classes,
                                std::set<std::string>* compiled_classes, size_t thread_count,
                                bool dump_stats, bool dump_passes, CumulativeLogger* timer,
-                               std::string profile_file)
-    : profile_present_(false), compiler_options_(compiler_options),
+                               int swap_fd, std::string profile_file)
+    : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
+      swap_space_allocator_(new SwapAllocator<void>(swap_space_.get())),
+      profile_present_(false), compiler_options_(compiler_options),
       verification_results_(verification_results),
       method_inliner_map_(method_inliner_map),
       compiler_(Compiler::Create(this, compiler_kind)),
@@ -347,6 +350,7 @@
       freezing_constructor_lock_("freezing constructor lock"),
       compiled_classes_lock_("compiled classes lock"),
       compiled_methods_lock_("compiled method lock"),
+      compiled_methods_(MethodTable::key_compare()),
       image_(image),
       image_classes_(image_classes),
       classes_to_compile_(compiled_classes),
@@ -362,11 +366,12 @@
       compiler_get_method_code_addr_(nullptr),
       support_boot_image_fixup_(instruction_set != kMips),
       cfi_info_(nullptr),
-      dedupe_code_("dedupe code"),
-      dedupe_mapping_table_("dedupe mapping table"),
-      dedupe_vmap_table_("dedupe vmap table"),
-      dedupe_gc_map_("dedupe gc map"),
-      dedupe_cfi_info_("dedupe cfi info") {
+      // Use actual deduping only if we don't use swap.
+      dedupe_code_("dedupe code", *swap_space_allocator_),
+      dedupe_mapping_table_("dedupe mapping table", *swap_space_allocator_),
+      dedupe_vmap_table_("dedupe vmap table", *swap_space_allocator_),
+      dedupe_gc_map_("dedupe gc map", *swap_space_allocator_),
+      dedupe_cfi_info_("dedupe cfi info", *swap_space_allocator_) {
   DCHECK(compiler_options_ != nullptr);
   DCHECK(verification_results_ != nullptr);
   DCHECK(method_inliner_map_ != nullptr);
@@ -400,27 +405,24 @@
   }
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateCode(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateCode(const ArrayRef<const uint8_t>& code) {
   return dedupe_code_.Add(Thread::Current(), code);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const ArrayRef<const uint8_t>& code) {
   return dedupe_mapping_table_.Add(Thread::Current(), code);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateVMapTable(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateVMapTable(const ArrayRef<const uint8_t>& code) {
   return dedupe_vmap_table_.Add(Thread::Current(), code);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateGCMap(const std::vector<uint8_t>& code) {
+SwapVector<uint8_t>* CompilerDriver::DeduplicateGCMap(const ArrayRef<const uint8_t>& code) {
   return dedupe_gc_map_.Add(Thread::Current(), code);
 }
 
-std::vector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info) {
-  if (cfi_info == nullptr) {
-    return nullptr;
-  }
-  return dedupe_cfi_info_.Add(Thread::Current(), *cfi_info);
+SwapVector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info) {
+  return dedupe_cfi_info_.Add(Thread::Current(), cfi_info);
 }
 
 CompilerDriver::~CompilerDriver() {
@@ -428,11 +430,14 @@
   {
     MutexLock mu(self, compiled_classes_lock_);
     STLDeleteValues(&compiled_classes_);
-    STLDeleteValues(&compiled_methods_);
     STLDeleteElements(&code_to_patch_);
     STLDeleteElements(&methods_to_patch_);
     STLDeleteElements(&classes_to_patch_);
     STLDeleteElements(&strings_to_patch_);
+
+    for (auto& pair : compiled_methods_) {
+      CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, pair.second);
+    }
   }
   CHECK_PTHREAD_CALL(pthread_key_delete, (tls_key_), "delete tls key");
   compiler_->UnInit();
@@ -2297,6 +2302,14 @@
   oss << " native alloc=" << PrettySize(allocated_space) << " free="
       << PrettySize(free_space);
 #endif
+  if (swap_space_.get() != nullptr) {
+    oss << " swap=" << PrettySize(swap_space_->GetSize());
+  }
+  oss << "\nCode dedupe: " << dedupe_code_.DumpStats();
+  oss << "\nMapping table dedupe: " << dedupe_mapping_table_.DumpStats();
+  oss << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats();
+  oss << "\nGC map dedupe: " << dedupe_gc_map_.DumpStats();
+  oss << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats();
   return oss.str();
 }
 
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 1618475..58285cc 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -39,6 +39,7 @@
 #include "thread_pool.h"
 #include "utils/arena_allocator.h"
 #include "utils/dedupe_set.h"
+#include "utils/swap_space.h"
 #include "dex/verified_method.h"
 
 namespace art {
@@ -91,6 +92,8 @@
     void* llvm_info_;
 };
 
+static constexpr bool kUseMurmur3Hash = true;
+
 class CompilerDriver {
  public:
   // Create a compiler targeting the requested "instruction_set".
@@ -107,7 +110,8 @@
                           bool image, std::set<std::string>* image_classes,
                           std::set<std::string>* compiled_classes,
                           size_t thread_count, bool dump_stats, bool dump_passes,
-                          CumulativeLogger* timer, std::string profile_file = "");
+                          CumulativeLogger* timer, int swap_fd = -1,
+                          std::string profile_file = "");
 
   ~CompilerDriver();
 
@@ -382,6 +386,9 @@
   const ArenaPool* GetArenaPool() const {
     return &arena_pool_;
   }
+  SwapAllocator<void>& GetSwapSpaceAllocator() {
+    return *swap_space_allocator_.get();
+  }
 
   bool WriteElf(const std::string& android_root,
                 bool is_host,
@@ -640,11 +647,11 @@
   void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
       LOCKS_EXCLUDED(compiled_classes_lock_);
 
-  std::vector<uint8_t>* DeduplicateCode(const std::vector<uint8_t>& code);
-  std::vector<uint8_t>* DeduplicateMappingTable(const std::vector<uint8_t>& code);
-  std::vector<uint8_t>* DeduplicateVMapTable(const std::vector<uint8_t>& code);
-  std::vector<uint8_t>* DeduplicateGCMap(const std::vector<uint8_t>& code);
-  std::vector<uint8_t>* DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info);
+  SwapVector<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code);
+  SwapVector<uint8_t>* DeduplicateMappingTable(const ArrayRef<const uint8_t>& code);
+  SwapVector<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& code);
+  SwapVector<uint8_t>* DeduplicateGCMap(const ArrayRef<const uint8_t>& code);
+  SwapVector<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info);
 
   /*
    * @brief return the pointer to the Call Frame Information.
@@ -654,9 +661,6 @@
     return cfi_info_.get();
   }
 
-  ProfileFile profile_file_;
-  bool profile_present_;
-
   // Should the compiler run on this method given profile information?
   bool SkipCompilation(const std::string& method_name);
 
@@ -755,6 +759,14 @@
   static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
 
+  // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first
+  // as other fields rely on this.
+  std::unique_ptr<SwapSpace> swap_space_;
+  std::unique_ptr<SwapAllocator<void> > swap_space_allocator_;
+
+  ProfileFile profile_file_;
+  bool profile_present_;
+
   std::vector<const CallPatchInformation*> code_to_patch_;
   std::vector<const CallPatchInformation*> methods_to_patch_;
   std::vector<const TypePatchInformation*> classes_to_patch_;
@@ -841,42 +853,81 @@
   // DeDuplication data structures, these own the corresponding byte arrays.
   class DedupeHashFunc {
    public:
-    size_t operator()(const std::vector<uint8_t>& array) const {
-      // For small arrays compute a hash using every byte.
-      static const size_t kSmallArrayThreshold = 16;
-      size_t hash = 0x811c9dc5;
-      if (array.size() <= kSmallArrayThreshold) {
+    size_t operator()(const ArrayRef<const uint8_t>& array) const {
+      if (kUseMurmur3Hash) {
+        static constexpr uint32_t c1 = 0xcc9e2d51;
+        static constexpr uint32_t c2 = 0x1b873593;
+        static constexpr uint32_t r1 = 15;
+        static constexpr uint32_t r2 = 13;
+        static constexpr uint32_t m = 5;
+        static constexpr uint32_t n = 0xe6546b64;
+
+        uint32_t hash = 0;
+        uint32_t len = static_cast<uint32_t>(array.size());
+
+        const int nblocks = len / 4;
+        typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+        const unaligned_uint32_t *blocks = reinterpret_cast<const uint32_t*>(array.data());
+        int i;
+        for (i = 0; i < nblocks; i++) {
+          uint32_t k = blocks[i];
+          k *= c1;
+          k = (k << r1) | (k >> (32 - r1));
+          k *= c2;
+
+          hash ^= k;
+          hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
+        }
+
+        const uint8_t *tail = reinterpret_cast<const uint8_t*>(array.data() + nblocks * 4);
+        uint32_t k1 = 0;
+
+        switch (len & 3) {
+          case 3:
+            k1 ^= tail[2] << 16;
+          case 2:
+            k1 ^= tail[1] << 8;
+          case 1:
+            k1 ^= tail[0];
+
+            k1 *= c1;
+            k1 = (k1 << r1) | (k1 >> (32 - r1));
+            k1 *= c2;
+            hash ^= k1;
+        }
+
+        hash ^= len;
+        hash ^= (hash >> 16);
+        hash *= 0x85ebca6b;
+        hash ^= (hash >> 13);
+        hash *= 0xc2b2ae35;
+        hash ^= (hash >> 16);
+
+        return hash;
+      } else {
+        size_t hash = 0x811c9dc5;
         for (uint8_t b : array) {
           hash = (hash * 16777619) ^ b;
         }
-      } else {
-        // For larger arrays use the 2 bytes at 6 bytes (the location of a push registers
-        // instruction field for quick generated code on ARM) and then select a number of other
-        // values at random.
-        static const size_t kRandomHashCount = 16;
-        for (size_t i = 0; i < 2; ++i) {
-          uint8_t b = array[i + 6];
-          hash = (hash * 16777619) ^ b;
-        }
-        for (size_t i = 2; i < kRandomHashCount; ++i) {
-          size_t r = i * 1103515245 + 12345;
-          uint8_t b = array[r % array.size()];
-          hash = (hash * 16777619) ^ b;
-        }
+        hash += hash << 13;
+        hash ^= hash >> 7;
+        hash += hash << 3;
+        hash ^= hash >> 17;
+        hash += hash << 5;
+        return hash;
       }
-      hash += hash << 13;
-      hash ^= hash >> 7;
-      hash += hash << 3;
-      hash ^= hash >> 17;
-      hash += hash << 5;
-      return hash;
     }
   };
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_code_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_mapping_table_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_vmap_table_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_gc_map_;
-  DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_cfi_info_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_code_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_mapping_table_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_vmap_table_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_gc_map_;
+  DedupeSet<ArrayRef<const uint8_t>,
+            SwapVector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_cfi_info_;
 
   DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
 };
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 35b7294..71ef239 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -434,12 +434,12 @@
   std::vector<uint8_t> managed_code(cs);
   MemoryRegion code(&managed_code[0], managed_code.size());
   __ FinalizeInstructions(code);
-  return new CompiledMethod(driver,
-                            instruction_set,
-                            managed_code,
-                            frame_size,
-                            main_jni_conv->CoreSpillMask(),
-                            main_jni_conv->FpSpillMask());
+  return CompiledMethod::SwapAllocCompiledMethod(driver,
+                                                 instruction_set,
+                                                 ArrayRef<const uint8_t>(managed_code),
+                                                 frame_size,
+                                                 main_jni_conv->CoreSpillMask(),
+                                                 main_jni_conv->FpSpillMask());
 }
 
 // Copy a single parameter from the managed to the JNI calling convention
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index fff581c..4694e0c 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -59,7 +59,7 @@
         EXPECT_EQ(oat_method.GetFpSpillMask(), compiled_method->GetFpSpillMask());
         uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(quick_oat_code), 2);
         quick_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
-        const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+        const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
         EXPECT_TRUE(quick_code != nullptr);
         size_t code_size = quick_code->size() * sizeof(quick_code[0]);
         EXPECT_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size))
@@ -73,7 +73,7 @@
         EXPECT_EQ(oat_method.GetFpSpillMask(), 0U);
         uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(portable_oat_code), 2);
         portable_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
-        const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
+        const SwapVector<uint8_t>* portable_code = compiled_method->GetPortableCode();
         EXPECT_TRUE(portable_code != nullptr);
         size_t code_size = portable_code->size() * sizeof(portable_code[0]);
         EXPECT_EQ(0, memcmp(quick_oat_code, &portable_code[0], code_size))
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index a32fac1..cab0573 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -138,7 +138,7 @@
 }
 
 struct OatWriter::GcMapDataAccess {
-  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+  static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
     return &compiled_method->GetGcMap();
   }
 
@@ -160,7 +160,7 @@
 };
 
 struct OatWriter::MappingTableDataAccess {
-  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+  static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
     return &compiled_method->GetMappingTable();
   }
 
@@ -182,7 +182,7 @@
 };
 
 struct OatWriter::VmapTableDataAccess {
-  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+  static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
     return &compiled_method->GetVmapTable();
   }
 
@@ -342,8 +342,8 @@
       // Derived from CompiledMethod.
       uint32_t quick_code_offset = 0;
 
-      const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
-      const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+      const SwapVector<uint8_t>* portable_code = compiled_method->GetPortableCode();
+      const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
       if (portable_code != nullptr) {
         CHECK(quick_code == nullptr);
         size_t oat_method_offsets_offset =
@@ -412,7 +412,7 @@
         std::vector<uint8_t>* cfi_info = writer_->compiler_driver_->GetCallFrameInformation();
         if (cfi_info != nullptr) {
           // Copy in the FDE, if present
-          const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo();
+          const SwapVector<uint8_t>* fde = compiled_method->GetCFIInfo();
           if (fde != nullptr) {
             // Copy the information into cfi_info and then fix the address in the new copy.
             int cur_offset = cfi_info->size();
@@ -465,7 +465,7 @@
         } else {
           status = mirror::Class::kStatusNotReady;
         }
-        const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
+        const SwapVector<uint8_t>& gc_map = compiled_method->GetGcMap();
         size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
         bool is_native = it.MemberIsNative();
         CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
@@ -505,7 +505,7 @@
       DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
       DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u);
 
-      const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+      const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method);
       uint32_t map_size = map->size() * sizeof((*map)[0]);
       if (map_size != 0u) {
         auto lb = dedupe_map_.lower_bound(map);
@@ -527,7 +527,7 @@
  private:
   // Deduplication is already done on a pointer basis by the compiler driver,
   // so we can simply compare the pointers to find out if things are duplicated.
-  SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_;
+  SafeMap<const SwapVector<uint8_t>*, uint32_t> dedupe_map_;
 };
 
 class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
@@ -583,7 +583,7 @@
       size_t file_offset = file_offset_;
       OutputStream* out = out_;
 
-      const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+      const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
       if (quick_code != nullptr) {
         CHECK(compiled_method->GetPortableCode() == nullptr);
         uint32_t aligned_offset = compiled_method->AlignCode(offset_);
@@ -668,7 +668,7 @@
       ++method_offsets_index_;
 
       // Write deduplicated map.
-      const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+      const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method);
       size_t map_size = map->size() * sizeof((*map)[0]);
       DCHECK((map_size == 0u && map_offset == 0u) ||
             (map_size != 0u && map_offset != 0u && map_offset <= offset_))
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 8a5077b..b9f7e8b 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -167,16 +167,16 @@
   std::vector<uint8_t> gc_map;
   codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
 
-  return new CompiledMethod(GetCompilerDriver(),
-                            instruction_set,
-                            allocator.GetMemory(),
-                            codegen->GetFrameSize(),
-                            codegen->GetCoreSpillMask(),
-                            0, /* FPR spill mask, unused */
-                            mapping_table,
-                            vmap_table,
-                            gc_map,
-                            nullptr);
+  return CompiledMethod::SwapAllocCompiledMethod(GetCompilerDriver(),
+                                                 instruction_set,
+                                                 ArrayRef<const uint8_t>(allocator.GetMemory()),
+                                                 codegen->GetFrameSize(),
+                                                 codegen->GetCoreSpillMask(),
+                                                 0, /* FPR spill mask, unused */
+                                                 ArrayRef<const uint8_t>(mapping_table),
+                                                 ArrayRef<const uint8_t>(vmap_table),
+                                                 ArrayRef<const uint8_t>(gc_map),
+                                                 ArrayRef<const uint8_t>());
 }
 
 }  // namespace art
diff --git a/compiler/utils/array_ref.h b/compiler/utils/array_ref.h
index 2d70b7d..0ab5434 100644
--- a/compiler/utils/array_ref.h
+++ b/compiler/utils/array_ref.h
@@ -88,7 +88,7 @@
 
   template <typename U>
   ArrayRef(const std::vector<U>& v,
-           typename std::enable_if<std::is_same<T, const U>::value, tag>::tag t = tag())
+           typename std::enable_if<std::is_same<T, const U>::value, tag>::type t = tag())
       : array_(v.data()), size_(v.size()) {
   }
 
diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h
index 4c52174..b062a2a 100644
--- a/compiler/utils/dedupe_set.h
+++ b/compiler/utils/dedupe_set.h
@@ -17,50 +17,89 @@
 #ifndef ART_COMPILER_UTILS_DEDUPE_SET_H_
 #define ART_COMPILER_UTILS_DEDUPE_SET_H_
 
+#include <algorithm>
+#include <inttypes.h>
+#include <memory>
 #include <set>
 #include <string>
 
 #include "base/mutex.h"
 #include "base/stl_util.h"
 #include "base/stringprintf.h"
+#include "utils/swap_space.h"
 
 namespace art {
 
 // A set of Keys that support a HashFunc returning HashType. Used to find duplicates of Key in the
 // Add method. The data-structure is thread-safe through the use of internal locks, it also
 // supports the lock being sharded.
-template <typename Key, typename HashType, typename HashFunc, HashType kShard = 1>
+template <typename InKey, typename StoreKey, typename HashType, typename HashFunc,
+          HashType kShard = 1>
 class DedupeSet {
-  typedef std::pair<HashType, Key*> HashedKey;
+  typedef std::pair<HashType, const InKey*> HashedInKey;
+  struct HashedKey {
+    StoreKey* store_ptr;
+    union {
+      HashType store_hash;        // Valid if store_ptr != nullptr.
+      const HashedInKey* in_key;  // Valid if store_ptr == nullptr.
+    };
+  };
 
   class Comparator {
    public:
     bool operator()(const HashedKey& a, const HashedKey& b) const {
-      if (a.first != b.first) {
-        return a.first < b.first;
+      HashType a_hash = (a.store_ptr != nullptr) ? a.store_hash : a.in_key->first;
+      HashType b_hash = (b.store_ptr != nullptr) ? b.store_hash : b.in_key->first;
+      if (a_hash != b_hash) {
+        return a_hash < b_hash;
+      }
+      if (a.store_ptr != nullptr && b.store_ptr != nullptr) {
+        return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
+                                            b.store_ptr->begin(), b.store_ptr->end());
+      } else if (a.store_ptr != nullptr && b.store_ptr == nullptr) {
+        return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
+                                            b.in_key->second->begin(), b.in_key->second->end());
+      } else if (a.store_ptr == nullptr && b.store_ptr != nullptr) {
+        return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
+                                            b.store_ptr->begin(), b.store_ptr->end());
       } else {
-        return *a.second < *b.second;
+        return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
+                                            b.in_key->second->begin(), b.in_key->second->end());
       }
     }
   };
 
  public:
-  Key* Add(Thread* self, const Key& key) {
+  StoreKey* Add(Thread* self, const InKey& key) {
+    uint64_t hash_start;
+    if (kIsDebugBuild) {
+      hash_start = NanoTime();
+    }
     HashType raw_hash = HashFunc()(key);
+    if (kIsDebugBuild) {
+      uint64_t hash_end = NanoTime();
+      hash_time_ += hash_end - hash_start;
+    }
     HashType shard_hash = raw_hash / kShard;
     HashType shard_bin = raw_hash % kShard;
-    HashedKey hashed_key(shard_hash, const_cast<Key*>(&key));
+    HashedInKey hashed_in_key(shard_hash, &key);
+    HashedKey hashed_key;
+    hashed_key.store_ptr = nullptr;
+    hashed_key.in_key = &hashed_in_key;
     MutexLock lock(self, *lock_[shard_bin]);
     auto it = keys_[shard_bin].find(hashed_key);
     if (it != keys_[shard_bin].end()) {
-      return it->second;
+      DCHECK(it->store_ptr != nullptr);
+      return it->store_ptr;
     }
-    hashed_key.second = new Key(key);
+    hashed_key.store_ptr = CreateStoreKey(key);
+    hashed_key.store_hash = shard_hash;
     keys_[shard_bin].insert(hashed_key);
-    return hashed_key.second;
+    return hashed_key.store_ptr;
   }
 
-  explicit DedupeSet(const char* set_name) {
+  explicit DedupeSet(const char* set_name, SwapAllocator<void>& alloc)
+      : allocator_(alloc), hash_time_(0) {
     for (HashType i = 0; i < kShard; ++i) {
       std::ostringstream oss;
       oss << set_name << " lock " << i;
@@ -70,15 +109,59 @@
   }
 
   ~DedupeSet() {
-    for (HashType i = 0; i < kShard; ++i) {
-      STLDeleteValues(&keys_[i]);
+    // Have to manually free all pointers.
+    for (auto& shard : keys_) {
+      for (const auto& hashed_key : shard) {
+        DCHECK(hashed_key.store_ptr != nullptr);
+        DeleteStoreKey(hashed_key.store_ptr);
+      }
     }
   }
 
+  std::string DumpStats() const {
+    size_t collision_sum = 0;
+    size_t collision_max = 0;
+    for (HashType shard = 0; shard < kShard; ++shard) {
+      HashType last_hash = 0;
+      size_t collision_cur_max = 0;
+      for (const HashedKey& key : keys_[shard]) {
+        DCHECK(key.store_ptr != nullptr);
+        if (key.store_hash == last_hash) {
+          collision_cur_max++;
+          if (collision_cur_max > 1) {
+            collision_sum++;
+            if (collision_cur_max > collision_max) {
+              collision_max = collision_cur_max;
+            }
+          }
+        } else {
+          collision_cur_max = 1;
+          last_hash = key.store_hash;
+        }
+      }
+    }
+    return StringPrintf("%zu collisions, %zu max bucket size, %" PRIu64 " ns hash time",
+                        collision_sum, collision_max, hash_time_);
+  }
+
  private:
+  StoreKey* CreateStoreKey(const InKey& key) {
+    StoreKey* ret = allocator_.allocate(1);
+    allocator_.construct(ret, key.begin(), key.end(), allocator_);
+    return ret;
+  }
+
+  void DeleteStoreKey(StoreKey* key) {
+    SwapAllocator<StoreKey> alloc(allocator_);
+    alloc.destroy(key);
+    alloc.deallocate(key, 1);
+  }
+
   std::string lock_name_[kShard];
   std::unique_ptr<Mutex> lock_[kShard];
   std::set<HashedKey, Comparator> keys_[kShard];
+  SwapAllocator<StoreKey> allocator_;
+  uint64_t hash_time_;
 
   DISALLOW_COPY_AND_ASSIGN(DedupeSet);
 };
diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc
index 8abe6de..637964e 100644
--- a/compiler/utils/dedupe_set_test.cc
+++ b/compiler/utils/dedupe_set_test.cc
@@ -15,6 +15,10 @@
  */
 
 #include "dedupe_set.h"
+
+#include <algorithm>
+#include <cstdio>
+
 #include "gtest/gtest.h"
 #include "thread-inl.h"
 
@@ -35,19 +39,22 @@
 TEST(DedupeSetTest, Test) {
   Thread* self = Thread::Current();
   typedef std::vector<uint8_t> ByteArray;
-  DedupeSet<ByteArray, size_t, DedupeHashFunc> deduplicator("test");
-  ByteArray* array1;
+  SwapAllocator<void> swap(nullptr);
+  DedupeSet<ByteArray, SwapVector<uint8_t>, size_t, DedupeHashFunc> deduplicator("test", swap);
+  SwapVector<uint8_t>* array1;
   {
     ByteArray test1;
     test1.push_back(10);
     test1.push_back(20);
     test1.push_back(30);
     test1.push_back(45);
+
     array1 = deduplicator.Add(self, test1);
-    ASSERT_EQ(test1, *array1);
+    ASSERT_NE(array1, nullptr);
+    ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array1->begin()));
   }
 
-  ByteArray* array2;
+  SwapVector<uint8_t>* array2;
   {
     ByteArray test1;
     test1.push_back(10);
@@ -56,10 +63,10 @@
     test1.push_back(45);
     array2 = deduplicator.Add(self, test1);
     ASSERT_EQ(array2, array1);
-    ASSERT_EQ(test1, *array2);
+    ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array2->begin()));
   }
 
-  ByteArray* array3;
+  SwapVector<uint8_t>* array3;
   {
     ByteArray test1;
     test1.push_back(10);
@@ -67,8 +74,8 @@
     test1.push_back(30);
     test1.push_back(47);
     array3 = deduplicator.Add(self, test1);
-    ASSERT_NE(array3, &test1);
-    ASSERT_EQ(test1, *array3);
+    ASSERT_NE(array3, nullptr);
+    ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array3->begin()));
   }
 }
 
diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc
new file mode 100644
index 0000000..44599f3
--- /dev/null
+++ b/compiler/utils/swap_space.cc
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "swap_space.h"
+
+#include <algorithm>
+#include <numeric>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "thread-inl.h"
+
+namespace art {
+
+// The chunk size by which the swap file is increased and mapped.
+static constexpr size_t kMininumMapSize = 16 * MB;
+
+static constexpr bool kCheckFreeMaps = false;
+
+template <typename FreeBySizeSet>
+static void DumpFreeMap(const FreeBySizeSet& free_by_size) {
+  size_t last_size = static_cast<size_t>(-1);
+  for (const auto& entry : free_by_size) {
+    if (last_size != entry.first) {
+      last_size = entry.first;
+      LOG(INFO) << "Size " << last_size;
+    }
+    LOG(INFO) << "  0x" << std::hex << entry.second->Start()
+        << " size=" << std::dec << entry.second->size;
+  }
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static void RemoveChunk(FreeByStartSet* free_by_start,
+                        FreeBySizeSet* free_by_size,
+                        typename FreeBySizeSet::const_iterator free_by_size_pos) {
+  auto free_by_start_pos = free_by_size_pos->second;
+  free_by_size->erase(free_by_size_pos);
+  free_by_start->erase(free_by_start_pos);
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static void InsertChunk(FreeByStartSet* free_by_start,
+                        FreeBySizeSet* free_by_size,
+                        const SpaceChunk& chunk) {
+  DCHECK_NE(chunk.size, 0u);
+  auto insert_result = free_by_start->insert(chunk);
+  DCHECK(insert_result.second);
+  free_by_size->emplace(chunk.size, insert_result.first);
+}
+
+SwapSpace::SwapSpace(int fd, size_t initial_size)
+    : fd_(fd),
+      size_(0),
+      lock_("SwapSpace lock", static_cast<LockLevel>(LockLevel::kDefaultMutexLevel - 1)) {
+  // Assume that the file is unlinked.
+
+  InsertChunk(&free_by_start_, &free_by_size_, NewFileChunk(initial_size));
+}
+
+SwapSpace::~SwapSpace() {
+  // All arenas are backed by the same file. Just close the descriptor.
+  close(fd_);
+}
+
+template <typename FreeByStartSet, typename FreeBySizeSet>
+static size_t CollectFree(const FreeByStartSet& free_by_start, const FreeBySizeSet& free_by_size) {
+  if (free_by_start.size() != free_by_size.size()) {
+    LOG(FATAL) << "Size: " << free_by_start.size() << " vs " << free_by_size.size();
+  }
+
+  // Calculate over free_by_size.
+  size_t sum1 = 0;
+  for (const auto& entry : free_by_size) {
+    sum1 += entry.second->size;
+  }
+
+  // Calculate over free_by_start.
+  size_t sum2 = 0;
+  for (const auto& entry : free_by_start) {
+    sum2 += entry.size;
+  }
+
+  if (sum1 != sum2) {
+    LOG(FATAL) << "Sum: " << sum1 << " vs " << sum2;
+  }
+  return sum1;
+}
+
+void* SwapSpace::Alloc(size_t size) {
+  MutexLock lock(Thread::Current(), lock_);
+  size = RoundUp(size, 8U);
+
+  // Check the free list for something that fits.
+  // TODO: Smarter implementation. Global biggest chunk, ...
+  SpaceChunk old_chunk;
+  auto it = free_by_start_.empty()
+      ? free_by_size_.end()
+      : free_by_size_.lower_bound(FreeBySizeEntry { size, free_by_start_.begin() });
+  if (it != free_by_size_.end()) {
+    old_chunk = *it->second;
+    RemoveChunk(&free_by_start_, &free_by_size_, it);
+  } else {
+    // Not a big enough free chunk, need to increase file size.
+    old_chunk = NewFileChunk(size);
+  }
+
+  void* ret = old_chunk.ptr;
+
+  if (old_chunk.size != size) {
+    // Insert the remainder.
+    SpaceChunk new_chunk = { old_chunk.ptr + size, old_chunk.size - size };
+    InsertChunk(&free_by_start_, &free_by_size_, new_chunk);
+  }
+
+  return ret;
+}
+
+SpaceChunk SwapSpace::NewFileChunk(size_t min_size) {
+  size_t next_part = std::max(RoundUp(min_size, kPageSize), RoundUp(kMininumMapSize, kPageSize));
+  int result = TEMP_FAILURE_RETRY(ftruncate64(fd_, size_ + next_part));
+  if (result != 0) {
+    PLOG(FATAL) << "Unable to increase swap file.";
+  }
+  uint8_t* ptr = reinterpret_cast<uint8_t*>(
+      mmap(nullptr, next_part, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, size_));
+  if (ptr == MAP_FAILED) {
+    LOG(ERROR) << "Unable to mmap new swap file chunk.";
+    LOG(ERROR) << "Current size: " << size_ << " requested: " << next_part << "/" << min_size;
+    LOG(ERROR) << "Free list:";
+    DumpFreeMap(free_by_size_);
+    LOG(ERROR) << "In free list: " << CollectFree(free_by_start_, free_by_size_);
+    LOG(FATAL) << "Aborting...";
+  }
+  size_ += next_part;
+  SpaceChunk new_chunk = {ptr, next_part};
+  maps_.push_back(new_chunk);
+  return new_chunk;
+}
+
+// TODO: Full coalescing.
+void SwapSpace::Free(void* ptrV, size_t size) {
+  MutexLock lock(Thread::Current(), lock_);
+  size = RoundUp(size, 8U);
+
+  size_t free_before = 0;
+  if (kCheckFreeMaps) {
+    free_before = CollectFree(free_by_start_, free_by_size_);
+  }
+
+  SpaceChunk chunk = { reinterpret_cast<uint8_t*>(ptrV), size };
+  auto it = free_by_start_.lower_bound(chunk);
+  if (it != free_by_start_.begin()) {
+    auto prev = it;
+    --prev;
+    CHECK_LE(prev->End(), chunk.Start());
+    if (prev->End() == chunk.Start()) {
+      // Merge *prev with this chunk.
+      chunk.size += prev->size;
+      chunk.ptr -= prev->size;
+      auto erase_pos = free_by_size_.find(FreeBySizeEntry { size, prev });
+      DCHECK(erase_pos != free_by_size_.end());
+      RemoveChunk(&free_by_start_, &free_by_size_, erase_pos);
+      // "prev" is invalidated but "it" remains valid.
+    }
+  }
+  if (it != free_by_start_.end()) {
+    CHECK_LE(chunk.End(), it->Start());
+    if (chunk.End() == it->Start()) {
+      // Merge *it with this chunk.
+      chunk.size += it->size;
+      auto erase_pos = free_by_size_.find(FreeBySizeEntry { size, it });
+      DCHECK(erase_pos != free_by_size_.end());
+      RemoveChunk(&free_by_start_, &free_by_size_, erase_pos);
+      // "it" is invalidated but we don't need it anymore.
+    }
+  }
+  InsertChunk(&free_by_start_, &free_by_size_, chunk);
+
+  if (kCheckFreeMaps) {
+    size_t free_after = CollectFree(free_by_start_, free_by_size_);
+
+    if (free_after != free_before + size) {
+      DumpFreeMap(free_by_size_);
+      CHECK_EQ(free_after, free_before + size) << "Should be " << size << " difference from " << free_before;
+    }
+  }
+}
+
+}  // namespace art
diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h
new file mode 100644
index 0000000..6c7f0b4
--- /dev/null
+++ b/compiler/utils/swap_space.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_UTILS_SWAP_SPACE_H_
+#define ART_COMPILER_UTILS_SWAP_SPACE_H_
+
+#include <cstdlib>
+#include <list>
+#include <set>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "mem_map.h"
+#include "utils.h"
+#include "utils/debug_stack.h"
+
+namespace art {
+
+// Chunk of space.
+struct SpaceChunk {
+  uint8_t* ptr;
+  size_t size;
+
+  uintptr_t Start() const {
+    return reinterpret_cast<uintptr_t>(ptr);
+  }
+  uintptr_t End() const {
+    return reinterpret_cast<uintptr_t>(ptr) + size;
+  }
+};
+
+inline bool operator==(const SpaceChunk& lhs, const SpaceChunk& rhs) {
+  return (lhs.size == rhs.size) && (lhs.ptr == rhs.ptr);
+}
+
+class SortChunkByPtr {
+ public:
+  bool operator()(const SpaceChunk& a, const SpaceChunk& b) const {
+    return reinterpret_cast<uintptr_t>(a.ptr) < reinterpret_cast<uintptr_t>(b.ptr);
+  }
+};
+
+// An arena pool that creates arenas backed by an mmaped file.
+class SwapSpace {
+ public:
+  SwapSpace(int fd, size_t initial_size);
+  ~SwapSpace();
+  void* Alloc(size_t size) LOCKS_EXCLUDED(lock_);
+  void Free(void* ptr, size_t size) LOCKS_EXCLUDED(lock_);
+
+  size_t GetSize() {
+    return size_;
+  }
+
+ private:
+  SpaceChunk NewFileChunk(size_t min_size);
+
+  int fd_;
+  size_t size_;
+  std::list<SpaceChunk> maps_;
+
+  // NOTE: Boost.Bimap would be useful for the two following members.
+
+  // Map start of a free chunk to its size.
+  typedef std::set<SpaceChunk, SortChunkByPtr> FreeByStartSet;
+  FreeByStartSet free_by_start_ GUARDED_BY(lock_);
+
+  // Map size to an iterator to free_by_start_'s entry.
+  typedef std::pair<size_t, FreeByStartSet::const_iterator> FreeBySizeEntry;
+  struct FreeBySizeComparator {
+    bool operator()(const FreeBySizeEntry& lhs, const FreeBySizeEntry& rhs) {
+      if (lhs.first != rhs.first) {
+        return lhs.first < rhs.first;
+      } else {
+        return lhs.second->Start() < rhs.second->Start();
+      }
+    }
+  };
+  typedef std::set<FreeBySizeEntry, FreeBySizeComparator> FreeBySizeSet;
+  FreeBySizeSet free_by_size_ GUARDED_BY(lock_);
+
+  mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  DISALLOW_COPY_AND_ASSIGN(SwapSpace);
+};
+
+template <typename T> class SwapAllocator;
+
+template <>
+class SwapAllocator<void> {
+ public:
+  typedef void value_type;
+  typedef void* pointer;
+  typedef const void* const_pointer;
+
+  template <typename U>
+  struct rebind {
+    typedef SwapAllocator<U> other;
+  };
+
+  explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+  template <typename U>
+  SwapAllocator(const SwapAllocator<U>& other) : swap_space_(other.swap_space_) {}
+
+  SwapAllocator(const SwapAllocator& other) = default;
+  SwapAllocator& operator=(const SwapAllocator& other) = default;
+  ~SwapAllocator() = default;
+
+ private:
+  SwapSpace* swap_space_;
+
+  template <typename U>
+  friend class SwapAllocator;
+};
+
+template <typename T>
+class SwapAllocator {
+ public:
+  typedef T value_type;
+  typedef T* pointer;
+  typedef T& reference;
+  typedef const T* const_pointer;
+  typedef const T& const_reference;
+  typedef size_t size_type;
+  typedef ptrdiff_t difference_type;
+
+  template <typename U>
+  struct rebind {
+    typedef SwapAllocator<U> other;
+  };
+
+  explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+  template <typename U>
+  SwapAllocator(const SwapAllocator<U>& other) : swap_space_(other.swap_space_) {}
+
+  SwapAllocator(const SwapAllocator& other) = default;
+  SwapAllocator& operator=(const SwapAllocator& other) = default;
+  ~SwapAllocator() = default;
+
+  size_type max_size() const {
+    return static_cast<size_type>(-1) / sizeof(T);
+  }
+
+  pointer address(reference x) const { return &x; }
+  const_pointer address(const_reference x) const { return &x; }
+
+  pointer allocate(size_type n, SwapAllocator<void>::pointer hint = nullptr) {
+    DCHECK_LE(n, max_size());
+    if (swap_space_ == nullptr) {
+      return reinterpret_cast<T*>(malloc(n * sizeof(T)));
+    } else {
+      return reinterpret_cast<T*>(swap_space_->Alloc(n * sizeof(T)));
+    }
+  }
+  void deallocate(pointer p, size_type n) {
+    if (swap_space_ == nullptr) {
+      free(p);
+    } else {
+      swap_space_->Free(p, n * sizeof(T));
+    }
+  }
+
+  void construct(pointer p, const_reference val) {
+    new (static_cast<void*>(p)) value_type(val);
+  }
+  template <class U, class... Args>
+  void construct(U* p, Args&&... args) {
+    ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
+  }
+  void destroy(pointer p) {
+    p->~value_type();
+  }
+
+  inline bool operator==(SwapAllocator const& other) {
+    return swap_space_ == other.swap_space_;
+  }
+  inline bool operator!=(SwapAllocator const& other) {
+    return !operator==(other);
+  }
+
+ private:
+  SwapSpace* swap_space_;
+
+  template <typename U>
+  friend class SwapAllocator;
+};
+
+template <typename T>
+using SwapVector = std::vector<T, SwapAllocator<T>>;
+template <typename T, typename Comparator>
+using SwapSet = std::set<T, Comparator, SwapAllocator<T>>;
+
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_SWAP_SPACE_H_
diff --git a/compiler/utils/swap_space_test.cc b/compiler/utils/swap_space_test.cc
new file mode 100644
index 0000000..bf50ac3
--- /dev/null
+++ b/compiler/utils/swap_space_test.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/swap_space.h"
+
+#include <cstdio>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "gtest/gtest.h"
+
+#include "base/unix_file/fd_file.h"
+#include "common_runtime_test.h"
+#include "os.h"
+
+namespace art {
+
+class SwapSpaceTest : public CommonRuntimeTest {
+};
+
+static void SwapTest(bool use_file) {
+  ScratchFile scratch;
+  int fd = scratch.GetFd();
+  unlink(scratch.GetFilename().c_str());
+
+  SwapSpace pool(fd, 1 * MB);
+  SwapAllocator<void> alloc(use_file ? &pool : nullptr);
+
+  SwapVector<int32_t> v(alloc);
+  v.reserve(1000000);
+  for (int32_t i = 0; i < 1000000; ++i) {
+    v.push_back(i);
+    EXPECT_EQ(i, v[i]);
+  }
+
+  SwapVector<int32_t> v2(alloc);
+  v2.reserve(1000000);
+  for (int32_t i = 0; i < 1000000; ++i) {
+    v2.push_back(i);
+    EXPECT_EQ(i, v2[i]);
+  }
+
+  SwapVector<int32_t> v3(alloc);
+  v3.reserve(500000);
+  for (int32_t i = 0; i < 1000000; ++i) {
+    v3.push_back(i);
+    EXPECT_EQ(i, v2[i]);
+  }
+
+  // Verify contents.
+  for (int32_t i = 0; i < 1000000; ++i) {
+    EXPECT_EQ(i, v[i]);
+    EXPECT_EQ(i, v2[i]);
+    EXPECT_EQ(i, v3[i]);
+  }
+
+  scratch.Close();
+}
+
+TEST_F(SwapSpaceTest, Memory) {
+  SwapTest(false);
+}
+
+TEST_F(SwapSpaceTest, Swap) {
+  SwapTest(true);
+}
+
+}  // namespace art
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 7d48e451..3a06006 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -229,6 +229,12 @@
   UsageError("  --disable-passes=<pass-names>:  disable one or more passes separated by comma.");
   UsageError("      Example: --disable-passes=UseCount,BBOptimizations");
   UsageError("");
+  UsageError("  --swap-file=<file-name>:  specifies a file to use for swap.");
+  UsageError("      Example: --swap-file=/data/tmp/swap.001");
+  UsageError("");
+  UsageError("  --swap-fd=<file-descriptor>:  specifies a file to use for swap (by descriptor).");
+  UsageError("      Example: --swap-fd=10");
+  UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
 }
@@ -357,6 +363,7 @@
                                       bool dump_passes,
                                       TimingLogger& timings,
                                       CumulativeLogger& compiler_phases_timings,
+                                      int swap_fd,
                                       std::string profile_file,
                                       SafeMap<std::string, std::string>* key_value_store) {
     CHECK(key_value_store != nullptr);
@@ -392,6 +399,7 @@
                                                               dump_stats,
                                                               dump_passes,
                                                               &compiler_phases_timings,
+                                                              swap_fd,
                                                               profile_file));
 
     driver->GetCompiler()->SetBitcodeFileName(*driver.get(), bitcode_filename);
@@ -802,6 +810,25 @@
 #endif
 }
 
+static constexpr size_t kMinDexFilesForSwap = 2;
+static constexpr size_t kMinDexFileCumulativeSizeForSwap = 20 * MB;
+
+static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) {
+  if (is_image) {
+    // Don't use swap, we know generation should succeed, and we don't want to slow it down.
+    return false;
+  }
+  if (dex_files.size() < kMinDexFilesForSwap) {
+    // If there are less dex files than the threshold, assume it's gonna be fine.
+    return false;
+  }
+  size_t dex_files_size = 0;
+  for (const auto* dex_file : dex_files) {
+    dex_files_size += dex_file->GetHeader().file_size_;
+  }
+  return dex_files_size >= kMinDexFileCumulativeSizeForSwap;
+}
+
 static int dex2oat(int argc, char** argv) {
   b13564922();
 
@@ -876,6 +903,10 @@
   bool implicit_so_checks = false;
   bool implicit_suspend_checks = false;
 
+  // Swap file.
+  std::string swap_file_name;
+  int swap_fd = -1;  // No swap file descriptor;
+
   for (int i = 0; i < argc; i++) {
     const StringPiece option(argv[i]);
     const bool log_options = false;
@@ -1062,6 +1093,16 @@
       include_patch_information = true;
     } else if (option == "--no-include-patch-information") {
       include_patch_information = false;
+    } else if (option.starts_with("--swap-file=")) {
+      swap_file_name = option.substr(strlen("--swap-file=")).data();
+    } else if (option.starts_with("--swap-fd=")) {
+      const char* swap_fd_str = option.substr(strlen("--swap-fd=")).data();
+      if (!ParseInt(swap_fd_str, &swap_fd)) {
+        Usage("Failed to parse --swap-fd argument '%s' as an integer", swap_fd_str);
+      }
+      if (swap_fd < 0) {
+        Usage("--swap-fd passed a negative value %d", swap_fd);
+      }
     } else {
       Usage("Unknown argument %s", option.data());
     }
@@ -1264,6 +1305,25 @@
     return EXIT_FAILURE;
   }
 
+  // Swap file handling.
+  //
+  // If the swap fd is not -1, we assume this is the file descriptor of an open but unlinked file
+  // that we can use for swap.
+  //
+  // If the swap fd is -1 and we have a swap-file string, open the given file as a swap file. We
+  // will immediately unlink to satisfy the swap fd assumption.
+  std::unique_ptr<File> swap_file;
+  if (swap_fd == -1 && !swap_file_name.empty()) {
+    swap_file.reset(OS::CreateEmptyFile(swap_file_name.c_str()));
+    if (swap_file.get() == nullptr) {
+      PLOG(ERROR) << "Failed to create swap file: " << swap_file_name;
+      return EXIT_FAILURE;
+    }
+    swap_fd = swap_file->Fd();
+    swap_file->MarkUnchecked();  // We don't we to track this, it will be unlinked immediately.
+    unlink(swap_file_name.c_str());
+  }
+
   timings.StartTiming("dex2oat Setup");
   LOG(INFO) << CommandLine();
 
@@ -1294,6 +1354,11 @@
       std::make_pair("imageinstructionset",
                      reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
 
+  if (swap_fd != -1) {
+    // Swap file indicates low-memory mode. Use GC.
+    runtime_options.push_back(std::make_pair("-Xgc:MS", nullptr));
+  }
+
   Dex2Oat* p_dex2oat;
   if (!Dex2Oat::Create(&p_dex2oat,
                        runtime_options,
@@ -1424,6 +1489,16 @@
       PLOG(ERROR) << "Failed to make .dex file writeable '" << dex_file->GetLocation() << "'\n";
     }
   }
+  // If we use a swap file, ensure we are above the threshold to make it necessary.
+  if (swap_fd != -1) {
+    if (!UseSwap(image, dex_files)) {
+      close(swap_fd);
+      swap_fd = -1;
+      LOG(INFO) << "Decided to run without swap.";
+    } else {
+      LOG(INFO) << "Accepted running with swap.";
+    }
+  }
 
   /*
    * If we're not in interpret-only or verify-none mode, go ahead and compile small applications.
@@ -1476,6 +1551,7 @@
                                                                         dump_passes,
                                                                         timings,
                                                                         compiler_phases_timings,
+                                                                        swap_fd,
                                                                         profile_file,
                                                                         key_value_store.get()));
   if (compiler.get() == nullptr) {
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 3d38e67..96d9d31 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -241,4 +241,8 @@
   return (flush_result != 0) ? flush_result : close_result;
 }
 
+void FdFile::MarkUnchecked() {
+  guard_state_ = GuardState::kNoCheck;
+}
+
 }  // namespace unix_file
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 8db2ee4..ae1d3f7 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -84,6 +84,9 @@
     kNoCheck         // Do not check for the current file instance.
   };
 
+  // WARNING: Only use this when you know what you're doing!
+  void MarkUnchecked();
+
  protected:
   // If the guard state indicates checking (!=kNoCheck), go to the target state "target". Print the
   // given warning if the current state is or exceeds warn_threshold.
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 877d327..c299210 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -84,15 +84,19 @@
   return file_->Fd();
 }
 
-void ScratchFile::Unlink() {
-  if (!OS::FileExists(filename_.c_str())) {
-    return;
-  }
+void ScratchFile::Close() {
   if (file_.get() != nullptr) {
     if (file_->FlushCloseOrErase() != 0) {
       PLOG(WARNING) << "Error closing scratch file.";
     }
   }
+}
+
+void ScratchFile::Unlink() {
+  if (!OS::FileExists(filename_.c_str())) {
+    return;
+  }
+  Close();
   int unlink_result = unlink(filename_.c_str());
   CHECK_EQ(0, unlink_result);
 }
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 5b014b3..c19b30f 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -55,6 +55,7 @@
 
   int GetFd() const;
 
+  void Close();
   void Unlink();
 
  private:
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 37c579d..efc2001 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -27,6 +27,7 @@
 exe="${ANDROID_HOST_OUT}/bin/dalvikvm32"
 main="Main"
 DEX_VERIFY=""
+DEX2OAT_SWAP="n"
 
 while true; do
     if [ "x$1" = "x--quiet" ]; then
@@ -107,6 +108,9 @@
         option="$1"
         FLAGS="${FLAGS} $option"
         shift
+    elif [ "x$1" = "x--dex2oat-swap" ]; then
+        DEX2OAT_SWAP="y"
+        shift
     elif [ "x$1" = "x--" ]; then
         shift
         break
@@ -137,6 +141,10 @@
 export LD_LIBRARY_PATH="${ANDROID_ROOT}/lib"
 export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/lib"
 
+if [ "$DEX2OAT_SWAP" = "y" ]; then
+  COMPILER_FLAGS="${COMPILER_FLAGS} --swap-file=$ANDROID_DATA/dex2oat.swap"
+fi
+
 if [ "$DEBUGGER" = "y" ]; then
     PORT=8000
     msg "Waiting for jdb to connect:"
diff --git a/test/run-test b/test/run-test
index 5d3cbac..e48681a 100755
--- a/test/run-test
+++ b/test/run-test
@@ -187,6 +187,9 @@
     elif [ "x$1" = "x--always-clean" ]; then
         always_clean="yes"
         shift
+    elif [ "x$1" = "x--dex2oat-swap" ]; then
+        run_args="${run_args} --dex2oat-swap"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         usage="yes"
@@ -342,6 +345,7 @@
         echo "    --64                 Run the test in 64-bit mode"
         echo "    --trace              Run with method tracing"
         echo "    --always-clean       Delete the test files even if the test fails."
+        echo "    --dex2oat-swap       Use a dex2oat swap file."
     ) 1>&2
     exit 1
 fi