am 2cea26c1: am c6dfdace: Add buffering to ELF file generation

* commit '2cea26c18f01a2b0bb7bcbf8ded7ec7f9930eb85':
  Add buffering to ELF file generation
diff --git a/compiler/dex/arena_allocator.cc b/compiler/dex/arena_allocator.cc
index 3a3e385..36393e7 100644
--- a/compiler/dex/arena_allocator.cc
+++ b/compiler/dex/arena_allocator.cc
@@ -18,9 +18,14 @@
 #include "dex_file-inl.h"
 #include "arena_allocator.h"
 #include "base/logging.h"
+#include "base/mutex.h"
 
 namespace art {
 
+// Memmap is a bit slower than malloc according to my measurements.
+static constexpr bool kUseMemMap = false;
+static constexpr bool kUseMemSet = true && kUseMemMap;
+
 static const char* alloc_names[ArenaAllocator::kNumAllocKinds] = {
   "Misc       ",
   "BasicBlock ",
@@ -37,108 +42,144 @@
   "Preds      ",
 };
 
-ArenaAllocator::ArenaAllocator(size_t default_size)
-  : default_size_(default_size),
-    block_size_(default_size - sizeof(ArenaMemBlock)),
-    arena_head_(NULL),
-    current_block_(NULL),
-    num_arena_blocks_(0),
-    malloc_bytes_(0),
-    lost_bytes_(0),
-    num_allocations_(0) {
-  memset(&alloc_stats_[0], 0, sizeof(alloc_stats_));
-  // Start with an empty arena.
-  arena_head_ = current_block_ = EmptyArenaBlock();
-  num_arena_blocks_++;
-}
-
-ArenaAllocator::~ArenaAllocator() {
-  // Reclaim all the arena blocks allocated so far.
-  ArenaMemBlock* head = arena_head_;
-  while (head != NULL) {
-    ArenaMemBlock* p = head;
-    head = head->next;
-    free(p);
+Arena::Arena(size_t size)
+    : bytes_allocated_(0),
+      map_(nullptr),
+      next_(nullptr) {
+  if (kUseMemMap) {
+    map_ = MemMap::MapAnonymous("dalvik-arena", NULL, size, PROT_READ | PROT_WRITE);
+    memory_ = map_->Begin();
+    size_ = map_->Size();
+  } else {
+    memory_ = reinterpret_cast<uint8_t*>(calloc(1, size));
+    size_ = size;
   }
-  arena_head_ = NULL;
-  num_arena_blocks_ = 0;
 }
 
-// Return an arena with no storage for use as a sentinal.
-ArenaAllocator::ArenaMemBlock* ArenaAllocator::EmptyArenaBlock() {
-  ArenaMemBlock* res = static_cast<ArenaMemBlock*>(malloc(sizeof(ArenaMemBlock)));
-  malloc_bytes_ += sizeof(ArenaMemBlock);
-  res->block_size = 0;
-  res->bytes_allocated = 0;
-  res->next = NULL;
-  return res;
+Arena::~Arena() {
+  if (kUseMemMap) {
+    delete map_;
+  } else {
+    free(reinterpret_cast<void*>(memory_));
+  }
 }
 
-// Arena-based malloc for compilation tasks.
-void* ArenaAllocator::NewMem(size_t size, bool zero, ArenaAllocKind kind) {
-  DCHECK(current_block_ != NULL);
-  DCHECK(arena_head_ != NULL);
-  size = (size + 3) & ~3;
-  alloc_stats_[kind] += size;
-  ArenaMemBlock* allocation_block = current_block_;  // Assume we'll fit.
-  size_t remaining_space = current_block_->block_size - current_block_->bytes_allocated;
-  if (remaining_space < size) {
-    /*
-     * Time to allocate a new block.  If this is a large allocation or we have
-     * significant space remaining in the current block then fulfill the allocation
-     * request with a custom-sized malloc() - otherwise grab a new standard block.
-     */
-    size_t allocation_size = sizeof(ArenaMemBlock);
-    if ((remaining_space >= ARENA_HIGH_WATER) || (size > block_size_)) {
-      allocation_size += size;
+void Arena::Reset() {
+  if (bytes_allocated_) {
+    if (kUseMemSet || !kUseMemMap) {
+      memset(Begin(), 0, bytes_allocated_);
     } else {
-      allocation_size += block_size_;
+      madvise(Begin(), bytes_allocated_, MADV_DONTNEED);
     }
-    ArenaMemBlock *new_block = static_cast<ArenaMemBlock*>(malloc(allocation_size));
-    if (new_block == NULL) {
-      LOG(FATAL) << "Arena allocation failure";
-    }
-    malloc_bytes_ += allocation_size;
-    new_block->block_size = allocation_size - sizeof(ArenaMemBlock);
-    new_block->bytes_allocated = 0;
-    new_block->next = NULL;
-    num_arena_blocks_++;
-    /*
-     * If the new block is completely full, insert it into the head of the list so we don't
-     * bother trying to fit more and won't hide the potentially allocatable space on the
-     * last (current_block_) block.  TUNING: if we move to a mark scheme, revisit
-     * this code to keep allocation order intact.
-     */
-    if (new_block->block_size == size) {
-      new_block->next = arena_head_;
-      arena_head_ = new_block;
-    } else {
-      int lost = (current_block_->block_size - current_block_->bytes_allocated);
-      lost_bytes_ += lost;
-      current_block_->next = new_block;
-      current_block_ = new_block;
-    }
-    allocation_block = new_block;
+    bytes_allocated_ = 0;
   }
-  void* ptr = &allocation_block->ptr[allocation_block->bytes_allocated];
-  allocation_block->bytes_allocated += size;
-  if (zero) {
-    memset(ptr, 0, size);
-  }
-  num_allocations_++;
-  return ptr;
 }
 
-// Dump memory usage stats.
-void ArenaAllocator::DumpMemStats(std::ostream& os) const {
+ArenaPool::ArenaPool()
+    : lock_("Arena pool lock"),
+      free_arenas_(nullptr) {
+}
+
+ArenaPool::~ArenaPool() {
+  while (free_arenas_ != nullptr) {
+    auto* arena = free_arenas_;
+    free_arenas_ = free_arenas_->next_;
+    delete arena;
+  }
+}
+
+Arena* ArenaPool::AllocArena(size_t size) {
+  Thread* self = Thread::Current();
+  Arena* ret = nullptr;
+  {
+    MutexLock lock(self, lock_);
+    if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) {
+      ret = free_arenas_;
+      free_arenas_ = free_arenas_->next_;
+    }
+  }
+  if (ret == nullptr) {
+    ret = new Arena(size);
+  }
+  ret->Reset();
+  return ret;
+}
+
+void ArenaPool::FreeArena(Arena* arena) {
+  Thread* self = Thread::Current();
+  {
+    MutexLock lock(self, lock_);
+    arena->next_ = free_arenas_;
+    free_arenas_ = arena;
+  }
+}
+
+size_t ArenaAllocator::BytesAllocated() const {
   size_t total = 0;
   for (int i = 0; i < kNumAllocKinds; i++) {
     total += alloc_stats_[i];
   }
-  os << " MEM: used: " << total << ", allocated: " << malloc_bytes_
-     << ", lost: " << lost_bytes_ << "\n";
-  os << "Number of blocks allocated: " << num_arena_blocks_ << ", Number of allocations: "
-     << num_allocations_ << ", avg: " << total / num_allocations_ << "\n";
+  return total;
+}
+
+ArenaAllocator::ArenaAllocator(ArenaPool* pool)
+  : pool_(pool),
+    begin_(nullptr),
+    end_(nullptr),
+    ptr_(nullptr),
+    arena_head_(nullptr),
+    num_allocations_(0) {
+  memset(&alloc_stats_[0], 0, sizeof(alloc_stats_));
+}
+
+void ArenaAllocator::UpdateBytesAllocated() {
+  if (arena_head_ != nullptr) {
+    // Update how many bytes we have allocated into the arena so that the arena pool knows how
+    // much memory to zero out.
+    arena_head_->bytes_allocated_ = ptr_ - begin_;
+  }
+}
+
+ArenaAllocator::~ArenaAllocator() {
+  // Reclaim all the arenas by giving them back to the thread pool.
+  UpdateBytesAllocated();
+  while (arena_head_ != nullptr) {
+    Arena* arena = arena_head_;
+    arena_head_ = arena_head_->next_;
+    pool_->FreeArena(arena);
+  }
+}
+
+void ArenaAllocator::ObtainNewArenaForAllocation(size_t allocation_size) {
+  UpdateBytesAllocated();
+  Arena* new_arena = pool_->AllocArena(std::max(Arena::kDefaultSize, allocation_size));
+  new_arena->next_ = arena_head_;
+  arena_head_ = new_arena;
+  // Update our internal data structures.
+  ptr_ = begin_ = new_arena->Begin();
+  end_ = new_arena->End();
+}
+
+// Dump memory usage stats.
+void ArenaAllocator::DumpMemStats(std::ostream& os) const {
+  size_t malloc_bytes = 0;
+  // Start out with how many lost bytes we have in the arena we are currently allocating into.
+  size_t lost_bytes(end_ - ptr_);
+  size_t num_arenas = 0;
+  for (Arena* arena = arena_head_; arena != nullptr; arena = arena->next_) {
+    malloc_bytes += arena->Size();
+    if (arena != arena_head_) {
+      lost_bytes += arena->RemainingSpace();
+    }
+    ++num_arenas;
+  }
+  const size_t bytes_allocated = BytesAllocated();
+  os << " MEM: used: " << bytes_allocated << ", allocated: " << malloc_bytes
+     << ", lost: " << lost_bytes << "\n";
+  if (num_allocations_ != 0) {
+    os << "Number of arenas allocated: " << num_arenas << ", Number of allocations: "
+       << num_allocations_ << ", avg size: " << bytes_allocated / num_allocations_ << "\n";
+  }
   os << "===== Allocation by kind\n";
   for (int i = 0; i < kNumAllocKinds; i++) {
       os << alloc_names[i] << std::setw(10) << alloc_stats_[i] << "\n";
diff --git a/compiler/dex/arena_allocator.h b/compiler/dex/arena_allocator.h
index e8e2c02..dda52a2 100644
--- a/compiler/dex/arena_allocator.h
+++ b/compiler/dex/arena_allocator.h
@@ -19,65 +19,127 @@
 
 #include <stdint.h>
 #include <stddef.h>
+
+#include "base/mutex.h"
 #include "compiler_enums.h"
+#include "mem_map.h"
 
 namespace art {
 
-#define ARENA_DEFAULT_BLOCK_SIZE (256 * 1024)
-#define ARENA_HIGH_WATER (16 * 1024)
+class Arena;
+class ArenaPool;
+class ArenaAllocator;
 
-class ArenaAllocator {
-  public:
-    // Type of allocation for memory tuning.
-    enum ArenaAllocKind {
-      kAllocMisc,
-      kAllocBB,
-      kAllocLIR,
-      kAllocMIR,
-      kAllocDFInfo,
-      kAllocGrowableArray,
-      kAllocGrowableBitMap,
-      kAllocDalvikToSSAMap,
-      kAllocDebugInfo,
-      kAllocSuccessor,
-      kAllocRegAlloc,
-      kAllocData,
-      kAllocPredecessors,
-      kNumAllocKinds
-    };
-
-  explicit ArenaAllocator(size_t default_size = ARENA_DEFAULT_BLOCK_SIZE);
-  ~ArenaAllocator();
-  void* NewMem(size_t size, bool zero, ArenaAllocKind kind);
-  size_t BytesAllocated() {
-    return malloc_bytes_;
+class Arena {
+ public:
+  static constexpr size_t kDefaultSize = 128 * KB;
+  explicit Arena(size_t size = kDefaultSize);
+  ~Arena();
+  void Reset();
+  uint8_t* Begin() {
+    return memory_;
   }
 
+  uint8_t* End() {
+    return memory_ + size_;
+  }
+
+  size_t Size() const {
+    return size_;
+  }
+
+  size_t RemainingSpace() const {
+    return Size() - bytes_allocated_;
+  }
+
+ private:
+  size_t bytes_allocated_;
+  uint8_t* memory_;
+  size_t size_;
+  MemMap* map_;
+  Arena* next_;
+  friend class ArenaPool;
+  friend class ArenaAllocator;
+  DISALLOW_COPY_AND_ASSIGN(Arena);
+};
+
+class ArenaPool {
+ public:
+  ArenaPool();
+  ~ArenaPool();
+  Arena* AllocArena(size_t size);
+  void FreeArena(Arena* arena);
+
+ private:
+  Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  Arena* free_arenas_ GUARDED_BY(lock_);
+  DISALLOW_COPY_AND_ASSIGN(ArenaPool);
+};
+
+class ArenaAllocator {
+ public:
+  // Type of allocation for memory tuning.
+  enum ArenaAllocKind {
+    kAllocMisc,
+    kAllocBB,
+    kAllocLIR,
+    kAllocMIR,
+    kAllocDFInfo,
+    kAllocGrowableArray,
+    kAllocGrowableBitMap,
+    kAllocDalvikToSSAMap,
+    kAllocDebugInfo,
+    kAllocSuccessor,
+    kAllocRegAlloc,
+    kAllocData,
+    kAllocPredecessors,
+    kNumAllocKinds
+  };
+
+  static constexpr bool kCountAllocations = false;
+
+  explicit ArenaAllocator(ArenaPool* pool);
+  ~ArenaAllocator();
+
+  // Returns zeroed memory.
+  void* Alloc(size_t bytes, ArenaAllocKind kind) ALWAYS_INLINE {
+    bytes = (bytes + 3) & ~3;
+    if (UNLIKELY(ptr_ + bytes > end_)) {
+      // Obtain a new block.
+      ObtainNewArenaForAllocation(bytes);
+      if (UNLIKELY(ptr_ == nullptr)) {
+        return nullptr;
+      }
+    }
+    if (kCountAllocations) {
+      alloc_stats_[kind] += bytes;
+      ++num_allocations_;
+    }
+    uint8_t* ret = ptr_;
+    ptr_ += bytes;
+    return ret;
+  }
+
+  void ObtainNewArenaForAllocation(size_t allocation_size);
+  size_t BytesAllocated() const;
   void DumpMemStats(std::ostream& os) const;
 
-  private:
-    // Variable-length allocation block.
-    struct ArenaMemBlock {
-      size_t block_size;
-      size_t bytes_allocated;
-      ArenaMemBlock *next;
-      char ptr[0];
-    };
+ private:
+  void UpdateBytesAllocated();
 
-    ArenaMemBlock* EmptyArenaBlock();
+  ArenaPool* pool_;
+  uint8_t* begin_;
+  uint8_t* end_;
+  uint8_t* ptr_;
+  Arena* arena_head_;
 
-    size_t default_size_;                    // Smallest size of new allocation block.
-    size_t block_size_;                      // Amount of allocatable bytes on a default block.
-    ArenaMemBlock* arena_head_;              // Head of linked list of allocation blocks.
-    ArenaMemBlock* current_block_;           // NOTE: code assumes there's always at least 1 block.
-    int num_arena_blocks_;
-    uint32_t malloc_bytes_;                  // Number of actual bytes malloc'd
-    uint32_t alloc_stats_[kNumAllocKinds];   // Bytes used by various allocation kinds.
-    uint32_t lost_bytes_;                    // Lost memory at end of too-small region
-    uint32_t num_allocations_;
+  // Statistics.
+  size_t num_allocations_;
+  size_t alloc_stats_[kNumAllocKinds];   // Bytes used by various allocation kinds.
+
+  DISALLOW_COPY_AND_ASSIGN(ArenaAllocator);
 };  // ArenaAllocator
 
-
 struct MemStats {
    public:
      void Dump(std::ostream& os) const {
diff --git a/compiler/dex/arena_bit_vector.cc b/compiler/dex/arena_bit_vector.cc
index 724fdf8..3fa9295 100644
--- a/compiler/dex/arena_bit_vector.cc
+++ b/compiler/dex/arena_bit_vector.cc
@@ -35,8 +35,8 @@
      expandable_(expandable),
      kind_(kind),
      storage_size_((start_bits + 31) >> 5),
-     storage_(static_cast<uint32_t*>(arena_->NewMem(storage_size_ * sizeof(uint32_t), true,
-                                                    ArenaAllocator::kAllocGrowableBitMap))) {
+     storage_(static_cast<uint32_t*>(arena_->Alloc(storage_size_ * sizeof(uint32_t),
+                                                   ArenaAllocator::kAllocGrowableBitMap))) {
   DCHECK_EQ(sizeof(storage_[0]), 4U);    // Assuming 32-bit units.
 }
 
@@ -68,8 +68,8 @@
     unsigned int new_size = (num + 1 + 31) >> 5;
     DCHECK_GT(new_size, storage_size_);
     uint32_t *new_storage =
-        static_cast<uint32_t*>(arena_->NewMem(new_size * sizeof(uint32_t), false,
-                                              ArenaAllocator::kAllocGrowableBitMap));
+        static_cast<uint32_t*>(arena_->Alloc(new_size * sizeof(uint32_t),
+                                             ArenaAllocator::kAllocGrowableBitMap));
     memcpy(new_storage, storage_, storage_size_ * sizeof(uint32_t));
     // Zero out the new storage words.
     memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * sizeof(uint32_t));
diff --git a/compiler/dex/arena_bit_vector.h b/compiler/dex/arena_bit_vector.h
index 4ec8c88..8bcd628 100644
--- a/compiler/dex/arena_bit_vector.h
+++ b/compiler/dex/arena_bit_vector.h
@@ -67,8 +67,8 @@
         }
 
         static void* operator new(size_t size, ArenaAllocator* arena) {
-          return arena->NewMem(sizeof(ArenaBitVector::Iterator), true,
-                               ArenaAllocator::kAllocGrowableBitMap);
+          return arena->Alloc(sizeof(ArenaBitVector::Iterator),
+                              ArenaAllocator::kAllocGrowableBitMap);
         };
         static void operator delete(void* p) {}  // Nop.
 
@@ -84,7 +84,7 @@
     ~ArenaBitVector() {}
 
     static void* operator new(size_t size, ArenaAllocator* arena) {
-      return arena->NewMem(sizeof(ArenaBitVector), true, ArenaAllocator::kAllocGrowableBitMap);
+      return arena->Alloc(sizeof(ArenaBitVector), ArenaAllocator::kAllocGrowableBitMap);
     }
     static void operator delete(void* p) {}  // Nop.
 
diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h
index a9b5bf6..26d0923 100644
--- a/compiler/dex/compiler_ir.h
+++ b/compiler/dex/compiler_ir.h
@@ -43,7 +43,7 @@
 class Mir2Lir;
 
 struct CompilationUnit {
-  CompilationUnit()
+  explicit CompilationUnit(ArenaPool* pool)
     : compiler_driver(NULL),
       class_linker(NULL),
       dex_file(NULL),
@@ -66,6 +66,7 @@
       num_regs(0),
       num_compiler_temps(0),
       compiler_flip_match(false),
+      arena(pool),
       mir_graph(NULL),
       cg(NULL) {}
   /*
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index d1f7f3e..2303649 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -119,30 +119,30 @@
   VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
 
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  UniquePtr<CompilationUnit> cu(new CompilationUnit);
+  CompilationUnit cu(&compiler.GetArenaPool());
 
-  cu->compiler_driver = &compiler;
-  cu->class_linker = class_linker;
-  cu->instruction_set = compiler.GetInstructionSet();
-  cu->compiler_backend = compiler_backend;
-  DCHECK((cu->instruction_set == kThumb2) ||
-         (cu->instruction_set == kX86) ||
-         (cu->instruction_set == kMips));
+  cu.compiler_driver = &compiler;
+  cu.class_linker = class_linker;
+  cu.instruction_set = compiler.GetInstructionSet();
+  cu.compiler_backend = compiler_backend;
+  DCHECK((cu.instruction_set == kThumb2) ||
+         (cu.instruction_set == kX86) ||
+         (cu.instruction_set == kMips));
 
 
   /* Adjust this value accordingly once inlining is performed */
-  cu->num_dalvik_registers = code_item->registers_size_;
+  cu.num_dalvik_registers = code_item->registers_size_;
   // TODO: set this from command line
-  cu->compiler_flip_match = false;
-  bool use_match = !cu->compiler_method_match.empty();
-  bool match = use_match && (cu->compiler_flip_match ^
-      (PrettyMethod(method_idx, dex_file).find(cu->compiler_method_match) !=
+  cu.compiler_flip_match = false;
+  bool use_match = !cu.compiler_method_match.empty();
+  bool match = use_match && (cu.compiler_flip_match ^
+      (PrettyMethod(method_idx, dex_file).find(cu.compiler_method_match) !=
        std::string::npos));
   if (!use_match || match) {
-    cu->disable_opt = kCompilerOptimizerDisableFlags;
-    cu->enable_debug = kCompilerDebugFlags;
-    cu->verbose = VLOG_IS_ON(compiler) ||
-        (cu->enable_debug & (1 << kDebugVerbose));
+    cu.disable_opt = kCompilerOptimizerDisableFlags;
+    cu.enable_debug = kCompilerDebugFlags;
+    cu.verbose = VLOG_IS_ON(compiler) ||
+        (cu.enable_debug & (1 << kDebugVerbose));
   }
 
   /*
@@ -152,12 +152,12 @@
 
   if (compiler_backend == kPortable) {
     // Fused long branches not currently usseful in bitcode.
-    cu->disable_opt |= (1 << kBranchFusing);
+    cu.disable_opt |= (1 << kBranchFusing);
   }
 
-  if (cu->instruction_set == kMips) {
+  if (cu.instruction_set == kMips) {
     // Disable some optimizations for mips for now
-    cu->disable_opt |= (
+    cu.disable_opt |= (
         (1 << kLoadStoreElimination) |
         (1 << kLoadHoisting) |
         (1 << kSuppressLoads) |
@@ -170,72 +170,71 @@
         (1 << kPromoteCompilerTemps));
   }
 
-  cu->mir_graph.reset(new MIRGraph(cu.get(), &cu->arena));
+  cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena));
 
   /* Gathering opcode stats? */
   if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
-    cu->mir_graph->EnableOpcodeCounting();
+    cu.mir_graph->EnableOpcodeCounting();
   }
 
   /* Build the raw MIR graph */
-  cu->mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx,
+  cu.mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx,
                               class_loader, dex_file);
 
 #if !defined(ART_USE_PORTABLE_COMPILER)
-  if (cu->mir_graph->SkipCompilation(Runtime::Current()->GetCompilerFilter())) {
+  if (cu.mir_graph->SkipCompilation(Runtime::Current()->GetCompilerFilter())) {
     return NULL;
   }
 #endif
 
   /* Do a code layout pass */
-  cu->mir_graph->CodeLayout();
+  cu.mir_graph->CodeLayout();
 
   /* Perform SSA transformation for the whole method */
-  cu->mir_graph->SSATransformation();
+  cu.mir_graph->SSATransformation();
 
   /* Do constant propagation */
-  cu->mir_graph->PropagateConstants();
+  cu.mir_graph->PropagateConstants();
 
   /* Count uses */
-  cu->mir_graph->MethodUseCount();
+  cu.mir_graph->MethodUseCount();
 
   /* Perform null check elimination */
-  cu->mir_graph->NullCheckElimination();
+  cu.mir_graph->NullCheckElimination();
 
   /* Combine basic blocks where possible */
-  cu->mir_graph->BasicBlockCombine();
+  cu.mir_graph->BasicBlockCombine();
 
   /* Do some basic block optimizations */
-  cu->mir_graph->BasicBlockOptimization();
+  cu.mir_graph->BasicBlockOptimization();
 
-  if (cu->enable_debug & (1 << kDebugDumpCheckStats)) {
-    cu->mir_graph->DumpCheckStats();
+  if (cu.enable_debug & (1 << kDebugDumpCheckStats)) {
+    cu.mir_graph->DumpCheckStats();
   }
 
   if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
-    cu->mir_graph->ShowOpcodeStats();
+    cu.mir_graph->ShowOpcodeStats();
   }
 
   /* Set up regLocation[] array to describe values - one for each ssa_name. */
-  cu->mir_graph->BuildRegLocations();
+  cu.mir_graph->BuildRegLocations();
 
   CompiledMethod* result = NULL;
 
 #if defined(ART_USE_PORTABLE_COMPILER)
   if (compiler_backend == kPortable) {
-    cu->cg.reset(PortableCodeGenerator(cu.get(), cu->mir_graph.get(), &cu->arena,
-                                       llvm_compilation_unit));
+    cu.cg.reset(PortableCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena, llvm_compilation_unit));
   } else {
 #endif
     switch (compiler.GetInstructionSet()) {
       case kThumb2:
-        cu->cg.reset(ArmCodeGenerator(cu.get(), cu->mir_graph.get(), &cu->arena));
+        cu.cg.reset(ArmCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena));
         break;
       case kMips:
-        cu->cg.reset(MipsCodeGenerator(cu.get(), cu->mir_graph.get(), &cu->arena));
+        cu.cg.reset(MipsCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena));
         break;
       case kX86:
-        cu->cg.reset(X86CodeGenerator(cu.get(), cu->mir_graph.get(), &cu->arena));
+        cu.cg.reset(X86CodeGenerator(&cu, cu.mir_graph.get(), &cu.arena));
         break;
       default:
         LOG(FATAL) << "Unexpected instruction set: " << compiler.GetInstructionSet();
@@ -244,9 +243,9 @@
   }
 #endif
 
-  cu->cg->Materialize();
+  cu.cg->Materialize();
 
-  result = cu->cg->GetCompiledMethod();
+  result = cu.cg->GetCompiledMethod();
 
   if (result) {
     VLOG(compiler) << "Compiled " << PrettyMethod(method_idx, dex_file);
@@ -254,15 +253,15 @@
     VLOG(compiler) << "Deferred " << PrettyMethod(method_idx, dex_file);
   }
 
-  if (cu->enable_debug & (1 << kDebugShowMemoryUsage)) {
-    if (cu->arena.BytesAllocated() > (5 * 1024 *1024)) {
-      MemStats mem_stats(cu->arena);
+  if (cu.enable_debug & (1 << kDebugShowMemoryUsage)) {
+    if (cu.arena.BytesAllocated() > (5 * 1024 *1024)) {
+      MemStats mem_stats(cu.arena);
       LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats);
     }
   }
 
-  if (cu->enable_debug & (1 << kDebugShowSummaryMemoryUsage)) {
-    LOG(INFO) << "MEMINFO " << cu->arena.BytesAllocated() << " " << cu->mir_graph->GetNumBlocks()
+  if (cu.enable_debug & (1 << kDebugShowSummaryMemoryUsage)) {
+    LOG(INFO) << "MEMINFO " << cu.arena.BytesAllocated() << " " << cu.mir_graph->GetNumBlocks()
               << " " << PrettyMethod(method_idx, dex_file);
   }
 
diff --git a/compiler/dex/growable_array.h b/compiler/dex/growable_array.h
index 08a6478..8e2abfb 100644
--- a/compiler/dex/growable_array.h
+++ b/compiler/dex/growable_array.h
@@ -67,7 +67,7 @@
         }
 
         static void* operator new(size_t size, ArenaAllocator* arena) {
-          return arena->NewMem(sizeof(GrowableArray::Iterator), true, ArenaAllocator::kAllocGrowableArray);
+          return arena->Alloc(sizeof(GrowableArray::Iterator), ArenaAllocator::kAllocGrowableArray);
         };
         static void operator delete(void* p) {}  // Nop.
 
@@ -81,8 +81,8 @@
         num_allocated_(init_length),
         num_used_(0),
         kind_(kind) {
-      elem_list_ = static_cast<T*>(arena_->NewMem(sizeof(T) * init_length, true,
-                                                  ArenaAllocator::kAllocGrowableArray));
+      elem_list_ = static_cast<T*>(arena_->Alloc(sizeof(T) * init_length,
+                                                 ArenaAllocator::kAllocGrowableArray));
     };
 
 
@@ -95,8 +95,8 @@
       if (new_length > target_length) {
          target_length = new_length;
       }
-      T* new_array = static_cast<T*>(arena_->NewMem(sizeof(T) * target_length, true,
-                                                    ArenaAllocator::kAllocGrowableArray));
+      T* new_array = static_cast<T*>(arena_->Alloc(sizeof(T) * target_length,
+                                                   ArenaAllocator::kAllocGrowableArray));
       memcpy(new_array, elem_list_, sizeof(T) * num_allocated_);
       num_allocated_ = target_length;
       elem_list_ = new_array;
@@ -153,7 +153,7 @@
     T* GetRawStorage() const { return elem_list_; }
 
     static void* operator new(size_t size, ArenaAllocator* arena) {
-      return arena->NewMem(sizeof(GrowableArray<T>), true, ArenaAllocator::kAllocGrowableArray);
+      return arena->Alloc(sizeof(GrowableArray<T>), ArenaAllocator::kAllocGrowableArray);
     };
     static void operator delete(void* p) {}  // Nop.
 
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index c3a33ff..3a73717 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -954,11 +954,11 @@
   int i;
 
   mir->ssa_rep->num_uses = num_uses;
-  mir->ssa_rep->uses = static_cast<int*>(arena_->NewMem(sizeof(int) * num_uses, true,
-                                                        ArenaAllocator::kAllocDFInfo));
+  mir->ssa_rep->uses = static_cast<int*>(arena_->Alloc(sizeof(int) * num_uses,
+                                                       ArenaAllocator::kAllocDFInfo));
   // NOTE: will be filled in during type & size inference pass
-  mir->ssa_rep->fp_use = static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_uses, true,
-                                                           ArenaAllocator::kAllocDFInfo));
+  mir->ssa_rep->fp_use = static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_uses,
+                                                          ArenaAllocator::kAllocDFInfo));
 
   for (i = 0; i < num_uses; i++) {
     HandleSSAUse(mir->ssa_rep->uses, d_insn->arg[i], i);
@@ -972,11 +972,11 @@
   int i;
 
   mir->ssa_rep->num_uses = num_uses;
-  mir->ssa_rep->uses = static_cast<int*>(arena_->NewMem(sizeof(int) * num_uses, true,
-                                                        ArenaAllocator::kAllocDFInfo));
+  mir->ssa_rep->uses = static_cast<int*>(arena_->Alloc(sizeof(int) * num_uses,
+                                                       ArenaAllocator::kAllocDFInfo));
   // NOTE: will be filled in during type & size inference pass
-  mir->ssa_rep->fp_use = static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_uses, true,
-                                                           ArenaAllocator::kAllocDFInfo));
+  mir->ssa_rep->fp_use = static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_uses,
+                                                          ArenaAllocator::kAllocDFInfo));
 
   for (i = 0; i < num_uses; i++) {
     HandleSSAUse(mir->ssa_rep->uses, d_insn->vC+i, i);
@@ -991,8 +991,8 @@
 
   for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
     mir->ssa_rep =
-        static_cast<struct SSARepresentation *>(arena_->NewMem(sizeof(SSARepresentation), true,
-                                                               ArenaAllocator::kAllocDFInfo));
+        static_cast<struct SSARepresentation *>(arena_->Alloc(sizeof(SSARepresentation),
+                                                              ArenaAllocator::kAllocDFInfo));
 
     int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
 
@@ -1041,10 +1041,10 @@
 
     if (num_uses) {
       mir->ssa_rep->num_uses = num_uses;
-      mir->ssa_rep->uses = static_cast<int*>(arena_->NewMem(sizeof(int) * num_uses, false,
-                                                            ArenaAllocator::kAllocDFInfo));
-      mir->ssa_rep->fp_use = static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_uses, false,
-                                                               ArenaAllocator::kAllocDFInfo));
+      mir->ssa_rep->uses = static_cast<int*>(arena_->Alloc(sizeof(int) * num_uses,
+                                                           ArenaAllocator::kAllocDFInfo));
+      mir->ssa_rep->fp_use = static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_uses,
+                                                              ArenaAllocator::kAllocDFInfo));
     }
 
     int num_defs = 0;
@@ -1058,10 +1058,10 @@
 
     if (num_defs) {
       mir->ssa_rep->num_defs = num_defs;
-      mir->ssa_rep->defs = static_cast<int*>(arena_->NewMem(sizeof(int) * num_defs, false,
-                                                            ArenaAllocator::kAllocDFInfo));
-      mir->ssa_rep->fp_def = static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_defs, false,
-                                                               ArenaAllocator::kAllocDFInfo));
+      mir->ssa_rep->defs = static_cast<int*>(arena_->Alloc(sizeof(int) * num_defs,
+                                                           ArenaAllocator::kAllocDFInfo));
+      mir->ssa_rep->fp_def = static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_defs,
+                                                              ArenaAllocator::kAllocDFInfo));
     }
 
     DecodedInstruction *d_insn = &mir->dalvikInsn;
@@ -1109,8 +1109,8 @@
    * predecessor blocks.
    */
   bb->data_flow_info->vreg_to_ssa_map =
-      static_cast<int*>(arena_->NewMem(sizeof(int) * cu_->num_dalvik_registers, false,
-                                       ArenaAllocator::kAllocDFInfo));
+      static_cast<int*>(arena_->Alloc(sizeof(int) * cu_->num_dalvik_registers,
+                                      ArenaAllocator::kAllocDFInfo));
 
   memcpy(bb->data_flow_info->vreg_to_ssa_map, vreg_to_ssa_map_,
          sizeof(int) * cu_->num_dalvik_registers);
@@ -1146,12 +1146,12 @@
    * Dalvik register, and the SSA names for those are the same.
    */
   vreg_to_ssa_map_ =
-      static_cast<int*>(arena_->NewMem(sizeof(int) * num_dalvik_reg, false,
-                                       ArenaAllocator::kAllocDFInfo));
+      static_cast<int*>(arena_->Alloc(sizeof(int) * num_dalvik_reg,
+                                      ArenaAllocator::kAllocDFInfo));
   /* Keep track of the higest def for each dalvik reg */
   ssa_last_defs_ =
-      static_cast<int*>(arena_->NewMem(sizeof(int) * num_dalvik_reg, false,
-                                       ArenaAllocator::kAllocDFInfo));
+      static_cast<int*>(arena_->Alloc(sizeof(int) * num_dalvik_reg,
+                                      ArenaAllocator::kAllocDFInfo));
 
   for (unsigned int i = 0; i < num_dalvik_reg; i++) {
     vreg_to_ssa_map_[i] = i;
@@ -1174,8 +1174,8 @@
       bb->block_type == kEntryBlock ||
       bb->block_type == kExitBlock) {
       bb->data_flow_info =
-          static_cast<BasicBlockDataFlow*>(arena_->NewMem(sizeof(BasicBlockDataFlow), true,
-                                                          ArenaAllocator::kAllocDFInfo));
+          static_cast<BasicBlockDataFlow*>(arena_->Alloc(sizeof(BasicBlockDataFlow),
+                                                         ArenaAllocator::kAllocDFInfo));
       }
   }
 }
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 86f6ee5..81702e3 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -399,8 +399,8 @@
     BasicBlock *case_block = FindBlock(cur_offset + target_table[i], /* split */ true,
                                       /* create */ true, /* immed_pred_block_p */ &cur_block);
     SuccessorBlockInfo *successor_block_info =
-        static_cast<SuccessorBlockInfo*>(arena_->NewMem(sizeof(SuccessorBlockInfo), false,
-                                                        ArenaAllocator::kAllocSuccessor));
+        static_cast<SuccessorBlockInfo*>(arena_->Alloc(sizeof(SuccessorBlockInfo),
+                                                       ArenaAllocator::kAllocSuccessor));
     successor_block_info->block = case_block;
     successor_block_info->key =
         (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
@@ -444,7 +444,7 @@
         catches_.insert(catch_block->start_offset);
       }
       SuccessorBlockInfo *successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
-          (arena_->NewMem(sizeof(SuccessorBlockInfo), false, ArenaAllocator::kAllocSuccessor));
+          (arena_->Alloc(sizeof(SuccessorBlockInfo), ArenaAllocator::kAllocSuccessor));
       successor_block_info->block = catch_block;
       successor_block_info->key = iterator.GetHandlerTypeIndex();
       cur_block->successor_block_list.blocks->Insert(successor_block_info);
@@ -490,7 +490,7 @@
   new_block->start_offset = insn->offset;
   cur_block->fall_through = new_block;
   new_block->predecessors->Insert(cur_block);
-  MIR* new_insn = static_cast<MIR*>(arena_->NewMem(sizeof(MIR), true, ArenaAllocator::kAllocMIR));
+  MIR* new_insn = static_cast<MIR*>(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocMIR));
   *new_insn = *insn;
   insn->dalvikInsn.opcode =
       static_cast<Instruction::Code>(kMirOpCheck);
@@ -571,13 +571,12 @@
   int num_patterns = sizeof(special_patterns)/sizeof(special_patterns[0]);
   bool live_pattern = (num_patterns > 0) && !(cu_->disable_opt & (1 << kMatch));
   bool* dead_pattern =
-      static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_patterns, true,
-                                        ArenaAllocator::kAllocMisc));
+      static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_patterns, ArenaAllocator::kAllocMisc));
   int pattern_pos = 0;
 
   /* Parse all instructions and put them into containing basic blocks */
   while (code_ptr < code_end) {
-    MIR *insn = static_cast<MIR *>(arena_->NewMem(sizeof(MIR), true, ArenaAllocator::kAllocMIR));
+    MIR *insn = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocMIR));
     insn->offset = current_offset_;
     insn->m_unit_index = current_method_;
     int width = ParseInsn(code_ptr, &insn->dalvikInsn);
@@ -1002,7 +1001,7 @@
     str.append("]--optimized away");
   }
   int length = str.length() + 1;
-  ret = static_cast<char*>(arena_->NewMem(length, false, ArenaAllocator::kAllocDFInfo));
+  ret = static_cast<char*>(arena_->Alloc(length, ArenaAllocator::kAllocDFInfo));
   strncpy(ret, str.c_str(), length);
   return ret;
 }
@@ -1115,8 +1114,8 @@
  */
 CallInfo* MIRGraph::NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type,
                                   bool is_range) {
-  CallInfo* info = static_cast<CallInfo*>(arena_->NewMem(sizeof(CallInfo), true,
-                                                         ArenaAllocator::kAllocMisc));
+  CallInfo* info = static_cast<CallInfo*>(arena_->Alloc(sizeof(CallInfo),
+                                                        ArenaAllocator::kAllocMisc));
   MIR* move_result_mir = FindMoveResult(bb, mir);
   if (move_result_mir == NULL) {
     info->result.location = kLocInvalid;
@@ -1127,8 +1126,7 @@
   }
   info->num_arg_words = mir->ssa_rep->num_uses;
   info->args = (info->num_arg_words == 0) ? NULL : static_cast<RegLocation*>
-      (arena_->NewMem(sizeof(RegLocation) * info->num_arg_words, false,
-                      ArenaAllocator::kAllocMisc));
+      (arena_->Alloc(sizeof(RegLocation) * info->num_arg_words, ArenaAllocator::kAllocMisc));
   for (int i = 0; i < info->num_arg_words; i++) {
     info->args[i] = GetRawSrc(mir, i);
   }
@@ -1142,8 +1140,8 @@
 
 // Allocate a new basic block.
 BasicBlock* MIRGraph::NewMemBB(BBType block_type, int block_id) {
-  BasicBlock* bb = static_cast<BasicBlock*>(arena_->NewMem(sizeof(BasicBlock), true,
-                                                           ArenaAllocator::kAllocBB));
+  BasicBlock* bb = static_cast<BasicBlock*>(arena_->Alloc(sizeof(BasicBlock),
+                                                          ArenaAllocator::kAllocBB));
   bb->block_type = block_type;
   bb->id = block_id;
   // TUNING: better estimate of the exit block predecessors?
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index c02deab..28ab283 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -426,8 +426,8 @@
   }
 
   void EnableOpcodeCounting() {
-    opcode_count_ = static_cast<int*>(arena_->NewMem(kNumPackedOpcodes * sizeof(int), true,
-                                      ArenaAllocator::kAllocMisc));
+    opcode_count_ = static_cast<int*>(arena_->Alloc(kNumPackedOpcodes * sizeof(int),
+                                                    ArenaAllocator::kAllocMisc));
   }
 
   void ShowOpcodeStats();
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 9f694de..b7611f8 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -94,8 +94,8 @@
 
 void MIRGraph::PropagateConstants() {
   is_constant_v_ = new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false);
-  constant_values_ = static_cast<int*>(arena_->NewMem(sizeof(int) * GetNumSSARegs(), true,
-                                                      ArenaAllocator::kAllocDFInfo));
+  constant_values_ = static_cast<int*>(arena_->Alloc(sizeof(int) * GetNumSSARegs(),
+                                                     ArenaAllocator::kAllocDFInfo));
   AllNodesIterator iter(this, false /* not iterative */);
   for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
     DoConstantPropogation(bb);
@@ -399,8 +399,7 @@
                 DCHECK_EQ(SelectKind(if_true), kSelectMove);
                 DCHECK_EQ(SelectKind(if_false), kSelectMove);
                 int* src_ssa =
-                    static_cast<int*>(arena_->NewMem(sizeof(int) * 3, false,
-                                                     ArenaAllocator::kAllocDFInfo));
+                    static_cast<int*>(arena_->Alloc(sizeof(int) * 3, ArenaAllocator::kAllocDFInfo));
                 src_ssa[0] = mir->ssa_rep->uses[0];
                 src_ssa[1] = if_true->ssa_rep->uses[0];
                 src_ssa[2] = if_false->ssa_rep->uses[0];
@@ -409,16 +408,14 @@
               }
               mir->ssa_rep->num_defs = 1;
               mir->ssa_rep->defs =
-                  static_cast<int*>(arena_->NewMem(sizeof(int) * 1, false,
-                                                   ArenaAllocator::kAllocDFInfo));
+                  static_cast<int*>(arena_->Alloc(sizeof(int) * 1, ArenaAllocator::kAllocDFInfo));
               mir->ssa_rep->fp_def =
-                  static_cast<bool*>(arena_->NewMem(sizeof(bool) * 1, false,
-                                                    ArenaAllocator::kAllocDFInfo));
+                  static_cast<bool*>(arena_->Alloc(sizeof(bool) * 1, ArenaAllocator::kAllocDFInfo));
               mir->ssa_rep->fp_def[0] = if_true->ssa_rep->fp_def[0];
               // Match type of uses to def.
               mir->ssa_rep->fp_use =
-                  static_cast<bool*>(arena_->NewMem(sizeof(bool) * mir->ssa_rep->num_uses, false,
-                                                    ArenaAllocator::kAllocDFInfo));
+                  static_cast<bool*>(arena_->Alloc(sizeof(bool) * mir->ssa_rep->num_uses,
+                                                   ArenaAllocator::kAllocDFInfo));
               for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
                 mir->ssa_rep->fp_use[i] = mir->ssa_rep->fp_def[0];
               }
@@ -805,8 +802,7 @@
 
 void MIRGraph::DumpCheckStats() {
   Checkstats* stats =
-      static_cast<Checkstats*>(arena_->NewMem(sizeof(Checkstats), true,
-                                              ArenaAllocator::kAllocDFInfo));
+      static_cast<Checkstats*>(arena_->Alloc(sizeof(Checkstats), ArenaAllocator::kAllocDFInfo));
   checkstats_ = stats;
   AllNodesIterator iter(this, false /* not iterative */);
   for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc
index 90cec75..7831cf6 100644
--- a/compiler/dex/portable/mir_to_gbc.cc
+++ b/compiler/dex/portable/mir_to_gbc.cc
@@ -1972,7 +1972,7 @@
 
     ::llvm::OwningPtr< ::llvm::tool_output_file> out_file(
         new ::llvm::tool_output_file(fname.c_str(), errmsg,
-                                   ::llvm::sys::fs::F_Binary));
+                                   ::llvm::raw_fd_ostream::F_Binary));
 
     if (!errmsg.empty()) {
       LOG(ERROR) << "Failed to create bitcode output file: " << errmsg;
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 2d8e24f..2dbe5f5 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -316,13 +316,12 @@
   }
   // Add the table to the list - we'll process it later
   SwitchTable *tab_rec =
-      static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
-                                               ArenaAllocator::kAllocData));
+      static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   int size = table[1];
-  tab_rec->targets = static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true,
-                                                       ArenaAllocator::kAllocLIR));
+  tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
+                                                      ArenaAllocator::kAllocLIR));
   switch_tables_.Insert(tab_rec);
 
   // Get the switch value
@@ -365,13 +364,12 @@
   }
   // Add the table to the list - we'll process it later
   SwitchTable *tab_rec =
-      static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
-                                               ArenaAllocator::kAllocData));
+      static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable),  ArenaAllocator::kAllocData));
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   int size = table[1];
   tab_rec->targets =
-      static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true, ArenaAllocator::kAllocLIR));
+      static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), ArenaAllocator::kAllocLIR));
   switch_tables_.Insert(tab_rec);
 
   // Get the switch value
@@ -419,8 +417,7 @@
   const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
   // Add the table to the list - we'll process it later
   FillArrayData *tab_rec =
-      static_cast<FillArrayData*>(arena_->NewMem(sizeof(FillArrayData), true,
-                                                 ArenaAllocator::kAllocData));
+      static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), ArenaAllocator::kAllocData));
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   uint16_t width = tab_rec->table[1];
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index f1ccfa0..291319f 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -26,7 +26,7 @@
     ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
 
     // Required for target - codegen helpers.
-    bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src,
+    bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src,
                                     RegLocation rl_dest, int lit);
     int LoadHelper(ThreadOffset offset);
     LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg);
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index c258019..f2ff58e 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -415,7 +415,7 @@
 };
 
 // Integer division by constant via reciprocal multiply (Hacker's Delight, 10-4)
-bool ArmMir2Lir::SmallLiteralDivide(Instruction::Code dalvik_opcode,
+bool ArmMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
                                     RegLocation rl_src, RegLocation rl_dest, int lit) {
   if ((lit < 0) || (lit >= static_cast<int>(sizeof(magic_table)/sizeof(magic_table[0])))) {
     return false;
@@ -425,7 +425,7 @@
     return false;
   }
   // Tuning: add rem patterns
-  if (dalvik_opcode != Instruction::DIV_INT_LIT8) {
+  if (!is_div) {
     return false;
   }
 
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 47d3d97..6cc3052 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -538,16 +538,14 @@
   int num_temps = sizeof(core_temps)/sizeof(*core_temps);
   int num_fp_regs = sizeof(FpRegs)/sizeof(*FpRegs);
   int num_fp_temps = sizeof(fp_temps)/sizeof(*fp_temps);
-  reg_pool_ = static_cast<RegisterPool*>(arena_->NewMem(sizeof(*reg_pool_), true,
-                                                        ArenaAllocator::kAllocRegAlloc));
+  reg_pool_ = static_cast<RegisterPool*>(arena_->Alloc(sizeof(*reg_pool_),
+                                                       ArenaAllocator::kAllocRegAlloc));
   reg_pool_->num_core_regs = num_regs;
   reg_pool_->core_regs = reinterpret_cast<RegisterInfo*>
-      (arena_->NewMem(num_regs * sizeof(*reg_pool_->core_regs), true,
-                     ArenaAllocator::kAllocRegAlloc));
+      (arena_->Alloc(num_regs * sizeof(*reg_pool_->core_regs), ArenaAllocator::kAllocRegAlloc));
   reg_pool_->num_fp_regs = num_fp_regs;
   reg_pool_->FPRegs = static_cast<RegisterInfo*>
-      (arena_->NewMem(num_fp_regs * sizeof(*reg_pool_->FPRegs), true,
-                      ArenaAllocator::kAllocRegAlloc));
+      (arena_->Alloc(num_fp_regs * sizeof(*reg_pool_->FPRegs), ArenaAllocator::kAllocRegAlloc));
   CompilerInitPool(reg_pool_->core_regs, core_regs, reg_pool_->num_core_regs);
   CompilerInitPool(reg_pool_->FPRegs, FpRegs, reg_pool_->num_fp_regs);
   // Keep special registers from being allocated
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 5f6f3d5..d89f1ed 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -321,7 +321,7 @@
 LIR* Mir2Lir::AddWordData(LIR* *constant_list_p, int value) {
   /* Add the constant to the literal pool */
   if (constant_list_p) {
-    LIR* new_value = static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocData));
+    LIR* new_value = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocData));
     new_value->operands[0] = value;
     new_value->next = *constant_list_p;
     *constant_list_p = new_value;
@@ -793,7 +793,7 @@
   if (it == boundary_map_.end()) {
     LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
   }
-  LIR* new_label = static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocLIR));
+  LIR* new_label = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR));
   new_label->dalvik_offset = vaddr;
   new_label->opcode = kPseudoCaseLabel;
   new_label->operands[0] = keyVal;
@@ -961,8 +961,8 @@
       first_lir_insn_(NULL),
       last_lir_insn_(NULL) {
   promotion_map_ = static_cast<PromotionMap*>
-      (arena_->NewMem((cu_->num_dalvik_registers  + cu_->num_compiler_temps + 1) *
-                      sizeof(promotion_map_[0]), true, ArenaAllocator::kAllocRegAlloc));
+      (arena_->Alloc((cu_->num_dalvik_registers  + cu_->num_compiler_temps + 1) *
+                      sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc));
 }
 
 void Mir2Lir::Materialize() {
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index e5c7fb1..f018c61 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -1358,25 +1358,23 @@
 
 // Returns true if it added instructions to 'cu' to divide 'rl_src' by 'lit'
 // and store the result in 'rl_dest'.
-bool Mir2Lir::HandleEasyDivide(Instruction::Code dalvik_opcode,
+bool Mir2Lir::HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
                                RegLocation rl_src, RegLocation rl_dest, int lit) {
   if ((lit < 2) || ((cu_->instruction_set != kThumb2) && !IsPowerOfTwo(lit))) {
     return false;
   }
   // No divide instruction for Arm, so check for more special cases
   if ((cu_->instruction_set == kThumb2) && !IsPowerOfTwo(lit)) {
-    return SmallLiteralDivide(dalvik_opcode, rl_src, rl_dest, lit);
+    return SmallLiteralDivRem(dalvik_opcode, is_div, rl_src, rl_dest, lit);
   }
   int k = LowestSetBit(lit);
   if (k >= 30) {
     // Avoid special cases.
     return false;
   }
-  bool div = (dalvik_opcode == Instruction::DIV_INT_LIT8 ||
-      dalvik_opcode == Instruction::DIV_INT_LIT16);
   rl_src = LoadValue(rl_src, kCoreReg);
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
-  if (div) {
+  if (is_div) {
     int t_reg = AllocTemp();
     if (lit == 2) {
       // Division by 2 is by far the most common division by constant.
@@ -1544,17 +1542,17 @@
         GenImmedCheck(kCondAl, 0, 0, kThrowDivZero);
         return;
       }
-      if (HandleEasyDivide(opcode, rl_src, rl_dest, lit)) {
-        return;
-      }
-      if ((opcode == Instruction::DIV_INT_LIT8) ||
-          (opcode == Instruction::DIV_INT) ||
+      if ((opcode == Instruction::DIV_INT) ||
           (opcode == Instruction::DIV_INT_2ADDR) ||
+          (opcode == Instruction::DIV_INT_LIT8) ||
           (opcode == Instruction::DIV_INT_LIT16)) {
         is_div = true;
       } else {
         is_div = false;
       }
+      if (HandleEasyDivRem(opcode, is_div, rl_src, rl_dest, lit)) {
+        return;
+      }
       if (cu_->instruction_set == kMips) {
         rl_src = LoadValue(rl_src, kCoreReg);
         rl_result = GenDivRemLit(rl_dest, rl_src.low_reg, lit, is_div);
diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc
index 2e9c845..630e990 100644
--- a/compiler/dex/quick/local_optimizations.cc
+++ b/compiler/dex/quick/local_optimizations.cc
@@ -249,7 +249,7 @@
         /* Only sink store instructions */
         if (sink_distance && !is_this_lir_load) {
           LIR* new_store_lir =
-              static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocLIR));
+              static_cast<LIR*>(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR));
           *new_store_lir = *this_lir;
           /*
            * Stop point found - insert *before* the check_lir
@@ -446,7 +446,7 @@
       if (slot >= 0) {
         LIR* cur_lir = prev_inst_list[slot];
         LIR* new_load_lir =
-          static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocLIR));
+          static_cast<LIR*>(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR));
         *new_load_lir = *this_lir;
         /*
          * Insertion is guaranteed to succeed since check_lir
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index eaae0e1..d53c012 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -67,13 +67,12 @@
   }
   // Add the table to the list - we'll process it later
   SwitchTable *tab_rec =
-      static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
-                                               ArenaAllocator::kAllocData));
+      static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   int elements = table[1];
   tab_rec->targets =
-      static_cast<LIR**>(arena_->NewMem(elements * sizeof(LIR*), true, ArenaAllocator::kAllocLIR));
+      static_cast<LIR**>(arena_->Alloc(elements * sizeof(LIR*), ArenaAllocator::kAllocLIR));
   switch_tables_.Insert(tab_rec);
 
   // The table is composed of 8-byte key/disp pairs
@@ -147,12 +146,11 @@
   }
   // Add the table to the list - we'll process it later
   SwitchTable *tab_rec =
-      static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
-                                               ArenaAllocator::kAllocData));
+      static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   int size = table[1];
-  tab_rec->targets = static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true,
+  tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
                                                        ArenaAllocator::kAllocLIR));
   switch_tables_.Insert(tab_rec);
 
@@ -228,8 +226,8 @@
   const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
   // Add the table to the list - we'll process it later
   FillArrayData *tab_rec =
-      reinterpret_cast<FillArrayData*>(arena_->NewMem(sizeof(FillArrayData), true,
-                                                      ArenaAllocator::kAllocData));
+      reinterpret_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData),
+                                                     ArenaAllocator::kAllocData));
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   uint16_t width = tab_rec->table[1];
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 6100396..b9cb720 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -27,7 +27,7 @@
     MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
 
     // Required for target - codegen utilities.
-    bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src,
+    bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src,
                                     RegLocation rl_dest, int lit);
     int LoadHelper(ThreadOffset offset);
     LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg);
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index 4a48c87..6ce5750 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -314,7 +314,7 @@
   return OpCmpImmBranch(c_code, reg, 0, target);
 }
 
-bool MipsMir2Lir::SmallLiteralDivide(Instruction::Code dalvik_opcode,
+bool MipsMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
                                      RegLocation rl_src, RegLocation rl_dest, int lit) {
   LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips";
   return false;
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index 7a9e91a..4ee5b23 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -462,16 +462,14 @@
   int num_temps = sizeof(core_temps)/sizeof(*core_temps);
   int num_fp_regs = sizeof(FpRegs)/sizeof(*FpRegs);
   int num_fp_temps = sizeof(fp_temps)/sizeof(*fp_temps);
-  reg_pool_ = static_cast<RegisterPool*>(arena_->NewMem(sizeof(*reg_pool_), true,
-                                                        ArenaAllocator::kAllocRegAlloc));
+  reg_pool_ = static_cast<RegisterPool*>(arena_->Alloc(sizeof(*reg_pool_),
+                                                       ArenaAllocator::kAllocRegAlloc));
   reg_pool_->num_core_regs = num_regs;
   reg_pool_->core_regs = static_cast<RegisterInfo*>
-     (arena_->NewMem(num_regs * sizeof(*reg_pool_->core_regs), true,
-                     ArenaAllocator::kAllocRegAlloc));
+     (arena_->Alloc(num_regs * sizeof(*reg_pool_->core_regs), ArenaAllocator::kAllocRegAlloc));
   reg_pool_->num_fp_regs = num_fp_regs;
   reg_pool_->FPRegs = static_cast<RegisterInfo*>
-      (arena_->NewMem(num_fp_regs * sizeof(*reg_pool_->FPRegs), true,
-                      ArenaAllocator::kAllocRegAlloc));
+      (arena_->Alloc(num_fp_regs * sizeof(*reg_pool_->FPRegs), ArenaAllocator::kAllocRegAlloc));
   CompilerInitPool(reg_pool_->core_regs, core_regs, reg_pool_->num_core_regs);
   CompilerInitPool(reg_pool_->FPRegs, FpRegs, reg_pool_->num_fp_regs);
   // Keep special registers from being allocated
diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h
index d9aef5d..440df2a 100644
--- a/compiler/dex/quick/mir_to_lir-inl.h
+++ b/compiler/dex/quick/mir_to_lir-inl.h
@@ -40,7 +40,7 @@
 
 inline LIR* Mir2Lir::RawLIR(int dalvik_offset, int opcode, int op0,
                             int op1, int op2, int op3, int op4, LIR* target) {
-  LIR* insn = static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocLIR));
+  LIR* insn = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR));
   insn->dalvik_offset = dalvik_offset;
   insn->opcode = opcode;
   insn->operands[0] = op0;
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index 862c1d7..c41feb1 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -812,8 +812,8 @@
 void Mir2Lir::MethodMIR2LIR() {
   // Hold the labels of each block.
   block_label_list_ =
-      static_cast<LIR*>(arena_->NewMem(sizeof(LIR) * mir_graph_->GetNumBlocks(), true,
-                                       ArenaAllocator::kAllocLIR));
+      static_cast<LIR*>(arena_->Alloc(sizeof(LIR) * mir_graph_->GetNumBlocks(),
+                                      ArenaAllocator::kAllocLIR));
 
   PreOrderDfsIterator iter(mir_graph_, false /* not iterative */);
   for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 517fc66..a37ebd1 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -376,7 +376,7 @@
     RegLocation GetReturn(bool is_float);
 
     // Shared by all targets - implemented in gen_common.cc.
-    bool HandleEasyDivide(Instruction::Code dalvik_opcode,
+    bool HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
                           RegLocation rl_src, RegLocation rl_dest, int lit);
     bool HandleEasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit);
     void HandleSuspendLaunchPads();
@@ -525,7 +525,7 @@
 
 
     // Required for target - codegen helpers.
-    virtual bool SmallLiteralDivide(Instruction::Code dalvik_opcode,
+    virtual bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
                                     RegLocation rl_src, RegLocation rl_dest, int lit) = 0;
     virtual int LoadHelper(ThreadOffset offset) = 0;
     virtual LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg) = 0;
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
index d59c986..71b74a4 100644
--- a/compiler/dex/quick/ralloc_util.cc
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -931,7 +931,12 @@
 static int SortCounts(const void *val1, const void *val2) {
   const Mir2Lir::RefCounts* op1 = reinterpret_cast<const Mir2Lir::RefCounts*>(val1);
   const Mir2Lir::RefCounts* op2 = reinterpret_cast<const Mir2Lir::RefCounts*>(val2);
-  return (op1->count == op2->count) ? 0 : (op1->count < op2->count ? 1 : -1);
+  // Note that we fall back to sorting on reg so we get stable output
+  // on differing qsort implementations (such as on host and target or
+  // between local host and build servers).
+  return (op1->count == op2->count)
+          ? (op1->s_reg - op2->s_reg)
+          : (op1->count < op2->count ? 1 : -1);
 }
 
 void Mir2Lir::DumpCounts(const RefCounts* arr, int size, const char* msg) {
@@ -966,11 +971,11 @@
    * to describe register live ranges for GC.
    */
   RefCounts *core_regs =
-      static_cast<RefCounts*>(arena_->NewMem(sizeof(RefCounts) * num_regs, true,
-                                             ArenaAllocator::kAllocRegAlloc));
+      static_cast<RefCounts*>(arena_->Alloc(sizeof(RefCounts) * num_regs,
+                                            ArenaAllocator::kAllocRegAlloc));
   RefCounts *FpRegs =
-      static_cast<RefCounts *>(arena_->NewMem(sizeof(RefCounts) * num_regs, true,
-                                              ArenaAllocator::kAllocRegAlloc));
+      static_cast<RefCounts *>(arena_->Alloc(sizeof(RefCounts) * num_regs,
+                                             ArenaAllocator::kAllocRegAlloc));
   // Set ssa names for original Dalvik registers
   for (int i = 0; i < dalvik_regs; i++) {
     core_regs[i].s_reg = FpRegs[i].s_reg = i;
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 6e3e55f..2be2aa9 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -74,13 +74,12 @@
   }
   // Add the table to the list - we'll process it later
   SwitchTable *tab_rec =
-      static_cast<SwitchTable *>(arena_->NewMem(sizeof(SwitchTable), true,
-                                                ArenaAllocator::kAllocData));
+      static_cast<SwitchTable *>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   int size = table[1];
-  tab_rec->targets = static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true,
-                                                       ArenaAllocator::kAllocLIR));
+  tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
+                                                      ArenaAllocator::kAllocLIR));
   switch_tables_.Insert(tab_rec);
 
   // Get the switch value
@@ -131,8 +130,7 @@
   const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
   // Add the table to the list - we'll process it later
   FillArrayData *tab_rec =
-      static_cast<FillArrayData*>(arena_->NewMem(sizeof(FillArrayData), true,
-                                                 ArenaAllocator::kAllocData));
+      static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), ArenaAllocator::kAllocData));
   tab_rec->table = table;
   tab_rec->vaddr = current_dalvik_offset_;
   uint16_t width = tab_rec->table[1];
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 21328d5..478654d 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -27,7 +27,7 @@
     X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
 
     // Required for target - codegen helpers.
-    bool SmallLiteralDivide(Instruction::Code dalvik_opcode, RegLocation rl_src,
+    bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src,
                                     RegLocation rl_dest, int lit);
     int LoadHelper(ThreadOffset offset);
     LIR* LoadBaseDisp(int rBase, int displacement, int r_dest, OpSize size, int s_reg);
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 377d134..14be7dd 100644
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -295,7 +295,7 @@
   return OpCmpImmBranch(c_code, reg, 0, target);
 }
 
-bool X86Mir2Lir::SmallLiteralDivide(Instruction::Code dalvik_opcode,
+bool X86Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div,
                                     RegLocation rl_src, RegLocation rl_dest, int lit) {
   LOG(FATAL) << "Unexpected use of smallLiteralDive in x86";
   return false;
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 699f3ae..26accab 100644
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -438,16 +438,16 @@
   int num_temps = sizeof(core_temps)/sizeof(*core_temps);
   int num_fp_regs = sizeof(FpRegs)/sizeof(*FpRegs);
   int num_fp_temps = sizeof(fp_temps)/sizeof(*fp_temps);
-  reg_pool_ = static_cast<RegisterPool*>(arena_->NewMem(sizeof(*reg_pool_), true,
-                                                        ArenaAllocator::kAllocRegAlloc));
+  reg_pool_ = static_cast<RegisterPool*>(arena_->Alloc(sizeof(*reg_pool_),
+                                                       ArenaAllocator::kAllocRegAlloc));
   reg_pool_->num_core_regs = num_regs;
   reg_pool_->core_regs =
-      static_cast<RegisterInfo*>(arena_->NewMem(num_regs * sizeof(*reg_pool_->core_regs), true,
-                                                ArenaAllocator::kAllocRegAlloc));
+      static_cast<RegisterInfo*>(arena_->Alloc(num_regs * sizeof(*reg_pool_->core_regs),
+                                               ArenaAllocator::kAllocRegAlloc));
   reg_pool_->num_fp_regs = num_fp_regs;
   reg_pool_->FPRegs =
-      static_cast<RegisterInfo *>(arena_->NewMem(num_fp_regs * sizeof(*reg_pool_->FPRegs), true,
-                                                 ArenaAllocator::kAllocRegAlloc));
+      static_cast<RegisterInfo *>(arena_->Alloc(num_fp_regs * sizeof(*reg_pool_->FPRegs),
+                                                ArenaAllocator::kAllocRegAlloc));
   CompilerInitPool(reg_pool_->core_regs, core_regs, reg_pool_->num_core_regs);
   CompilerInitPool(reg_pool_->FPRegs, FpRegs, reg_pool_->num_fp_regs);
   // Keep special registers from being allocated
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index 18d8e93..cd1602f 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -136,8 +136,8 @@
   int num_registers = cu_->num_dalvik_registers;
   /* Allocate num_dalvik_registers bit vector pointers */
   def_block_matrix_ = static_cast<ArenaBitVector**>
-      (arena_->NewMem(sizeof(ArenaBitVector *) * num_registers, true,
-                      ArenaAllocator::kAllocDFInfo));
+      (arena_->Alloc(sizeof(ArenaBitVector *) * num_registers,
+                     ArenaAllocator::kAllocDFInfo));
   int i;
 
   /* Initialize num_register vectors with num_blocks bits each */
@@ -384,8 +384,8 @@
 
   /* Initalize & Clear i_dom_list */
   if (i_dom_list_ == NULL) {
-    i_dom_list_ = static_cast<int*>(arena_->NewMem(sizeof(int) * num_reachable_blocks, false,
-                                                   ArenaAllocator::kAllocDFInfo));
+    i_dom_list_ = static_cast<int*>(arena_->Alloc(sizeof(int) * num_reachable_blocks,
+                                                  ArenaAllocator::kAllocDFInfo));
   }
   for (int i = 0; i < num_reachable_blocks; i++) {
     i_dom_list_[i] = NOTVISITED;
@@ -564,7 +564,7 @@
         continue;
       }
       MIR *phi =
-          static_cast<MIR*>(arena_->NewMem(sizeof(MIR), true, ArenaAllocator::kAllocDFInfo));
+          static_cast<MIR*>(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocDFInfo));
       phi->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpPhi);
       phi->dalvikInsn.vA = dalvik_reg;
       phi->offset = phi_bb->start_offset;
@@ -610,14 +610,11 @@
     int num_uses = uses.size();
     mir->ssa_rep->num_uses = num_uses;
     mir->ssa_rep->uses =
-        static_cast<int*>(arena_->NewMem(sizeof(int) * num_uses, false,
-                                         ArenaAllocator::kAllocDFInfo));
+        static_cast<int*>(arena_->Alloc(sizeof(int) * num_uses, ArenaAllocator::kAllocDFInfo));
     mir->ssa_rep->fp_use =
-        static_cast<bool*>(arena_->NewMem(sizeof(bool) * num_uses, true,
-                                          ArenaAllocator::kAllocDFInfo));
+        static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_uses, ArenaAllocator::kAllocDFInfo));
     int* incoming =
-        static_cast<int*>(arena_->NewMem(sizeof(int) * num_uses, false,
-                                         ArenaAllocator::kAllocDFInfo));
+        static_cast<int*>(arena_->Alloc(sizeof(int) * num_uses, ArenaAllocator::kAllocDFInfo));
     // TODO: Ugly, rework (but don't burden each MIR/LIR for Phi-only needs)
     mir->dalvikInsn.vB = reinterpret_cast<uintptr_t>(incoming);
 
@@ -644,7 +641,7 @@
 
   /* Save SSA map snapshot */
   int* saved_ssa_map =
-      static_cast<int*>(arena_->NewMem(map_size, false, ArenaAllocator::kAllocDalvikToSSAMap));
+      static_cast<int*>(arena_->Alloc(map_size, ArenaAllocator::kAllocDalvikToSSAMap));
   memcpy(saved_ssa_map, vreg_to_ssa_map_, map_size);
 
   if (block->fall_through) {
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
index 5ee6753..07f37bb 100644
--- a/compiler/dex/vreg_analysis.cc
+++ b/compiler/dex/vreg_analysis.cc
@@ -374,8 +374,8 @@
  */
 void MIRGraph::BuildRegLocations() {
   /* Allocate the location map */
-  RegLocation* loc = static_cast<RegLocation*>(arena_->NewMem(GetNumSSARegs() * sizeof(*loc), true,
-                                                              ArenaAllocator::kAllocRegAlloc));
+  RegLocation* loc = static_cast<RegLocation*>(arena_->Alloc(GetNumSSARegs() * sizeof(*loc),
+                                                             ArenaAllocator::kAllocRegAlloc));
   for (int i = 0; i < GetNumSSARegs(); i++) {
     loc[i] = fresh_loc;
     loc[i].s_reg_low = i;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index c079e52..634d3bc 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -473,7 +473,7 @@
                                 const std::vector<const DexFile*>& dex_files,
                                 base::TimingLogger& timings) {
   DCHECK(!Runtime::Current()->IsStarted());
-  UniquePtr<ThreadPool> thread_pool(new ThreadPool(thread_count_));
+  UniquePtr<ThreadPool> thread_pool(new ThreadPool(thread_count_ - 1));
   PreCompile(class_loader, dex_files, *thread_pool.get(), timings);
   Compile(class_loader, dex_files, *thread_pool.get(), timings);
   if (dump_stats_) {
@@ -537,7 +537,7 @@
   std::vector<const DexFile*> dex_files;
   dex_files.push_back(dex_file);
 
-  UniquePtr<ThreadPool> thread_pool(new ThreadPool(1U));
+  UniquePtr<ThreadPool> thread_pool(new ThreadPool(0U));
   PreCompile(jclass_loader, dex_files, *thread_pool.get(), timings);
 
   uint32_t method_idx = method->GetDexMethodIndex();
@@ -1322,7 +1322,8 @@
                              CompilerDriver* compiler,
                              const DexFile* dex_file,
                              ThreadPool& thread_pool)
-    : class_linker_(class_linker),
+    : index_(0),
+      class_linker_(class_linker),
       class_loader_(class_loader),
       compiler_(compiler),
       dex_file_(dex_file),
@@ -1353,8 +1354,9 @@
     CHECK_GT(work_units, 0U);
 
     std::vector<ForAllClosure*> closures(work_units);
+    index_ = begin;
     for (size_t i = 0; i < work_units; ++i) {
-      closures[i] = new ForAllClosure(this, begin + i, end, callback, work_units);
+      closures[i] = new ForAllClosure(this, end, callback);
       thread_pool_->AddTask(self, closures[i]);
     }
     thread_pool_->StartWorkers(self);
@@ -1367,20 +1369,25 @@
     thread_pool_->Wait(self, true, false);
   }
 
+  size_t NextIndex() {
+    return index_.fetch_add(1);
+  }
+
  private:
   class ForAllClosure : public Task {
    public:
-    ForAllClosure(ParallelCompilationManager* manager, size_t begin, size_t end, Callback* callback,
-                  size_t stripe)
+    ForAllClosure(ParallelCompilationManager* manager, size_t end, Callback* callback)
         : manager_(manager),
-          begin_(begin),
           end_(end),
-          callback_(callback),
-          stripe_(stripe) {}
+          callback_(callback) {}
 
     virtual void Run(Thread* self) {
-      for (size_t i = begin_; i < end_; i += stripe_) {
-        callback_(manager_, i);
+      while (true) {
+        const size_t index = manager_->NextIndex();
+        if (UNLIKELY(index >= end_)) {
+          break;
+        }
+        callback_(manager_, index);
         self->AssertNoPendingException();
       }
     }
@@ -1390,18 +1397,19 @@
     }
 
    private:
-    const ParallelCompilationManager* const manager_;
-    const size_t begin_;
+    ParallelCompilationManager* const manager_;
     const size_t end_;
     const Callback* const callback_;
-    const size_t stripe_;
   };
 
+  AtomicInteger index_;
   ClassLinker* const class_linker_;
   const jobject class_loader_;
   CompilerDriver* const compiler_;
   const DexFile* const dex_file_;
   ThreadPool* const thread_pool_;
+
+  DISALLOW_COPY_AND_ASSIGN(ParallelCompilationManager);
 };
 
 // Return true if the class should be skipped during compilation. We
@@ -2094,7 +2102,7 @@
                 mirror::ObjectArray<mirror::ArtField>* fields = klass->GetSFields();
                 CHECK_EQ(fields->GetLength(), 1);
                 fields->Get(0)->SetObj(klass, manager->GetClassLinker()->FindPrimitiveClass('V'));
-                klass->SetStatus(mirror::Class::kStatusInitialized);
+                klass->SetStatus(mirror::Class::kStatusInitialized, soa.Self());
               } else {
                 manager->GetClassLinker()->EnsureInitialized(klass, true, true);
               }
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 22a510b..fa1b8f9 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -26,6 +26,7 @@
 #include "compiled_class.h"
 #include "compiled_method.h"
 #include "dex_file.h"
+#include "dex/arena_allocator.h"
 #include "instruction_set.h"
 #include "invoke_type.h"
 #include "method_reference.h"
@@ -213,6 +214,9 @@
     support_boot_image_fixup_ = support_boot_image_fixup;
   }
 
+  ArenaPool& GetArenaPool() {
+    return arena_pool_;
+  }
 
   bool WriteElf(const std::string& android_root,
                 bool is_host,
@@ -423,6 +427,9 @@
 
   pthread_key_t tls_key_;
 
+  // Arena pool used by the compiler.
+  ArenaPool arena_pool_;
+
   typedef void (*CompilerEnableAutoElfLoadingFn)(CompilerDriver& driver);
   CompilerEnableAutoElfLoadingFn compiler_enable_auto_elf_loading_;
 
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 60c8f07..04342fd 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -596,7 +596,7 @@
                 << " for " << elf_file_->GetPath();
     return false;
   }
-  if (!elf_file_->WriteFully(&dynstr[0], dynsym_size)) {
+  if (!elf_file_->WriteFully(&dynstr[0], dynstr_size)) {
     PLOG(ERROR) << "Failed to write .dynsym for " << elf_file_->GetPath();
     return false;
   }
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index dcafc19..106ef9a 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -152,7 +152,7 @@
       // non image classes should be in a space after the image.
       EXPECT_GT(reinterpret_cast<byte*>(klass), image_end) << descriptor;
     }
-    EXPECT_EQ(*klass->GetRawLockWordAddress(), 0);  // address should have been removed from monitor
+    EXPECT_TRUE(Monitor::IsValidLockWord(*klass->GetRawLockWordAddress()));
   }
 }
 
diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc
index 139100b..aa439cc 100644
--- a/compiler/llvm/llvm_compilation_unit.cc
+++ b/compiler/llvm/llvm_compilation_unit.cc
@@ -214,6 +214,7 @@
   ::llvm::TargetOptions target_options;
   target_options.FloatABIType = ::llvm::FloatABI::Soft;
   target_options.NoFramePointerElim = true;
+  target_options.NoFramePointerElimNonLeaf = true;
   target_options.UseSoftFloat = false;
   target_options.EnableFastISel = false;
 
@@ -257,7 +258,7 @@
 
     ::llvm::OwningPtr< ::llvm::tool_output_file> out_file(
       new ::llvm::tool_output_file(bitcode_filename_.c_str(), errmsg,
-                                 ::llvm::sys::fs::F_Binary));
+                                 ::llvm::raw_fd_ostream::F_Binary));
 
 
     if (!errmsg.empty()) {
@@ -277,6 +278,7 @@
   // pm_builder.Inliner = ::llvm::createAlwaysInlinerPass();
   // pm_builder.Inliner = ::llvm::createPartialInliningPass();
   pm_builder.OptLevel = 3;
+  pm_builder.DisableSimplifyLibCalls = 1;
   pm_builder.DisableUnitAtATime = 1;
   pm_builder.populateFunctionPassManager(fpm);
   pm_builder.populateModulePassManager(pm);
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 8bc7877..0227381 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -506,7 +506,7 @@
       typedef MappingTable::PcToDexIterator It;
       for (It cur = table.PcToDexBegin(), end = table.PcToDexEnd(); cur != end; ++cur) {
         if (offset == cur.NativePcOffset()) {
-          os << "suspend point dex PC: 0x" << cur.DexPc() << "\n";
+          os << StringPrintf("suspend point dex PC: 0x%04x\n", cur.DexPc());
           return cur.DexPc();
         }
       }
@@ -514,7 +514,7 @@
       typedef MappingTable::DexToPcIterator It;
       for (It cur = table.DexToPcBegin(), end = table.DexToPcEnd(); cur != end; ++cur) {
         if (offset == cur.NativePcOffset()) {
-          os << "catch entry dex PC: 0x" << cur.DexPc() << "\n";
+          os << StringPrintf("catch entry dex PC: 0x%04x\n", cur.DexPc());
           return cur.DexPc();
         }
       }
@@ -917,10 +917,10 @@
       os << StringPrintf("%p: java.lang.Class \"%s\" (", obj, PrettyDescriptor(klass).c_str())
          << klass->GetStatus() << ")\n";
     } else if (obj->IsArtField()) {
-      os << StringPrintf("%p: java.lang.reflect.Field %s\n", obj,
+      os << StringPrintf("%p: java.lang.reflect.ArtField %s\n", obj,
                          PrettyField(obj->AsArtField()).c_str());
     } else if (obj->IsArtMethod()) {
-      os << StringPrintf("%p: java.lang.reflect.Method %s\n", obj,
+      os << StringPrintf("%p: java.lang.reflect.ArtMethod %s\n", obj,
                          PrettyMethod(obj->AsArtMethod()).c_str());
     } else if (obj_class->IsStringClass()) {
       os << StringPrintf("%p: java.lang.String %s\n", obj,
diff --git a/runtime/base/stringpiece.h b/runtime/base/stringpiece.h
index 62088cc..91b83f6 100644
--- a/runtime/base/stringpiece.h
+++ b/runtime/base/stringpiece.h
@@ -208,19 +208,6 @@
 
 extern std::ostream& operator<<(std::ostream& o, const StringPiece& piece);
 
-struct StringPieceHash {
-  size_t operator()(const StringPiece& string_piece) const {
-    size_t string_size = string_piece.size();
-    const char* string_data = string_piece.data();
-    // This is the java.lang.String hashcode for convenience, not interoperability.
-    size_t hash = 0;
-    while (string_size--) {
-      hash = hash * 31 + *string_data++;
-    }
-    return hash;
-  }
-};
-
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_STRINGPIECE_H_
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 3387a70..cd4a720 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -187,6 +187,8 @@
 ClassLinker::ClassLinker(InternTable* intern_table)
     // dex_lock_ is recursive as it may be used in stack dumping.
     : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel),
+      dex_cache_image_class_lookup_required_(false),
+      failed_dex_cache_class_lookups_(0),
       class_roots_(NULL),
       array_iftable_(NULL),
       init_done_(false),
@@ -225,7 +227,7 @@
   CHECK(java_lang_Object.get() != NULL);
   // backfill Object as the super class of Class.
   java_lang_Class->SetSuperClass(java_lang_Object.get());
-  java_lang_Object->SetStatus(mirror::Class::kStatusLoaded);
+  java_lang_Object->SetStatus(mirror::Class::kStatusLoaded, self);
 
   // Object[] next to hold class roots.
   SirtRef<mirror::Class> object_array_class(self, AllocClass(self, java_lang_Class.get(), sizeof(mirror::Class)));
@@ -243,7 +245,7 @@
   SirtRef<mirror::Class> java_lang_String(self, AllocClass(self, java_lang_Class.get(), sizeof(mirror::StringClass)));
   mirror::String::SetClass(java_lang_String.get());
   java_lang_String->SetObjectSize(sizeof(mirror::String));
-  java_lang_String->SetStatus(mirror::Class::kStatusResolved);
+  java_lang_String->SetStatus(mirror::Class::kStatusResolved, self);
 
   // Create storage for root classes, save away our work so far (requires descriptors).
   class_roots_ = mirror::ObjectArray<mirror::Class>::Alloc(self, object_array_class.get(), kClassRootsMax);
@@ -281,7 +283,7 @@
       java_lang_DexCache(self, AllocClass(self, java_lang_Class.get(), sizeof(mirror::DexCacheClass)));
   SetClassRoot(kJavaLangDexCache, java_lang_DexCache.get());
   java_lang_DexCache->SetObjectSize(sizeof(mirror::DexCacheClass));
-  java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved);
+  java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved, self);
 
   // Constructor, Field, Method, and AbstractMethod are necessary so that FindClass can link members.
   SirtRef<mirror::Class> java_lang_reflect_ArtField(self, AllocClass(self, java_lang_Class.get(),
@@ -289,7 +291,7 @@
   CHECK(java_lang_reflect_ArtField.get() != NULL);
   java_lang_reflect_ArtField->SetObjectSize(sizeof(mirror::ArtField));
   SetClassRoot(kJavaLangReflectArtField, java_lang_reflect_ArtField.get());
-  java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusResolved);
+  java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusResolved, self);
   mirror::ArtField::SetClass(java_lang_reflect_ArtField.get());
 
   SirtRef<mirror::Class> java_lang_reflect_ArtMethod(self, AllocClass(self, java_lang_Class.get(),
@@ -297,7 +299,7 @@
   CHECK(java_lang_reflect_ArtMethod.get() != NULL);
   java_lang_reflect_ArtMethod->SetObjectSize(sizeof(mirror::ArtMethod));
   SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.get());
-  java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved);
+  java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self);
 
   mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.get());
 
@@ -334,15 +336,15 @@
   SetClassRoot(kPrimitiveChar, char_class.get());  // needs descriptor
 
   // Object, String and DexCache need to be rerun through FindSystemClass to finish init
-  java_lang_Object->SetStatus(mirror::Class::kStatusNotReady);
+  java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self);
   mirror::Class* Object_class = FindSystemClass("Ljava/lang/Object;");
   CHECK_EQ(java_lang_Object.get(), Object_class);
   CHECK_EQ(java_lang_Object->GetObjectSize(), sizeof(mirror::Object));
-  java_lang_String->SetStatus(mirror::Class::kStatusNotReady);
+  java_lang_String->SetStatus(mirror::Class::kStatusNotReady, self);
   mirror::Class* String_class = FindSystemClass("Ljava/lang/String;");
   CHECK_EQ(java_lang_String.get(), String_class);
   CHECK_EQ(java_lang_String->GetObjectSize(), sizeof(mirror::String));
-  java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady);
+  java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady, self);
   mirror::Class* DexCache_class = FindSystemClass("Ljava/lang/DexCache;");
   CHECK_EQ(java_lang_String.get(), String_class);
   CHECK_EQ(java_lang_DexCache.get(), DexCache_class);
@@ -397,15 +399,15 @@
   CHECK_EQ(java_lang_Cloneable, kh.GetDirectInterface(0));
   CHECK_EQ(java_io_Serializable, kh.GetDirectInterface(1));
   // Run Class, ArtField, and ArtMethod through FindSystemClass. This initializes their
-  // dex_cache_ fields and register them in classes_.
+  // dex_cache_ fields and register them in class_table_.
   mirror::Class* Class_class = FindSystemClass("Ljava/lang/Class;");
   CHECK_EQ(java_lang_Class.get(), Class_class);
 
-  java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusNotReady);
+  java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusNotReady, self);
   mirror::Class* Art_method_class = FindSystemClass("Ljava/lang/reflect/ArtMethod;");
   CHECK_EQ(java_lang_reflect_ArtMethod.get(), Art_method_class);
 
-  java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusNotReady);
+  java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusNotReady, self);
   mirror::Class* Art_field_class = FindSystemClass("Ljava/lang/reflect/ArtField;");
   CHECK_EQ(java_lang_reflect_ArtField.get(), Art_field_class);
 
@@ -984,21 +986,14 @@
   return oat_file;
 }
 
-static void InitFromImageCallbackCommon(mirror::Object* obj, ClassLinker* class_linker,
-                                        bool interpret_only_mode)
+static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
+
   DCHECK(obj != NULL);
   DCHECK(class_linker != NULL);
 
-  if (obj->GetClass()->IsStringClass()) {
-    class_linker->GetInternTable()->RegisterStrong(obj->AsString());
-  } else if (obj->IsClass()) {
-    // Restore class to ClassLinker::classes_ table.
-    mirror::Class* klass = obj->AsClass();
-    ClassHelper kh(klass, class_linker);
-    mirror::Class* existing = class_linker->InsertClass(kh.GetDescriptor(), klass, true);
-    DCHECK(existing == NULL) << kh.GetDescriptor();
-  } else if (interpret_only_mode && obj->IsArtMethod()) {
+  if (obj->IsArtMethod()) {
     mirror::ArtMethod* method = obj->AsArtMethod();
     if (!method->IsNative()) {
       method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
@@ -1009,24 +1004,13 @@
   }
 }
 
-static void InitFromImageCallback(mirror::Object* obj, void* arg)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
-  InitFromImageCallbackCommon(obj, class_linker, false);
-}
-
-static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
-  InitFromImageCallbackCommon(obj, class_linker, true);
-}
-
 void ClassLinker::InitFromImage() {
   VLOG(startup) << "ClassLinker::InitFromImage entering";
   CHECK(!init_done_);
 
   gc::Heap* heap = Runtime::Current()->GetHeap();
   gc::space::ImageSpace* space = heap->GetImageSpace();
+  dex_cache_image_class_lookup_required_ = true;
   CHECK(space != NULL);
   OatFile& oat_file = GetImageOatFile(space);
   CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
@@ -1049,7 +1033,7 @@
   CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(),
            static_cast<uint32_t>(dex_caches->GetLength()));
   Thread* self = Thread::Current();
-  for (int i = 0; i < dex_caches->GetLength(); i++) {
+  for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
     SirtRef<mirror::DexCache> dex_cache(self, dex_caches->Get(i));
     const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
     const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location);
@@ -1069,15 +1053,11 @@
   // bitmap walk.
   mirror::ArtMethod::SetClass(GetClassRoot(kJavaLangReflectArtMethod));
 
-  // reinit clases_ table
-  {
+  // Set entry point to interpreter if in InterpretOnly mode.
+  if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
     ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
     heap->FlushAllocStack();
-    if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
-      heap->GetLiveBitmap()->Walk(InitFromImageInterpretOnlyCallback, this);
-    } else {
-      heap->GetLiveBitmap()->Walk(InitFromImageCallback, this);
-    }
+    heap->GetLiveBitmap()->Walk(InitFromImageInterpretOnlyCallback, this);
   }
 
   // reinit class_roots_
@@ -1120,7 +1100,7 @@
 
   {
     ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
-    for (const std::pair<size_t, mirror::Class*>& it : classes_) {
+    for (const std::pair<size_t, mirror::Class*>& it : class_table_) {
       visitor(it.second, arg);
     }
 
@@ -1134,14 +1114,12 @@
   }
 }
 
-void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) const {
-  ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  for (const std::pair<size_t, mirror::Class*>& it : classes_) {
-    if (!visitor(it.second, arg)) {
-      return;
-    }
+void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) {
+  if (dex_cache_image_class_lookup_required_) {
+    MoveImageClassesToClassTable();
   }
-  for (const std::pair<size_t, mirror::Class*>& it : image_classes_) {
+  ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  for (const std::pair<size_t, mirror::Class*>& it : class_table_) {
     if (!visitor(it.second, arg)) {
       return;
     }
@@ -1154,7 +1132,7 @@
   return true;
 }
 
-void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) const {
+void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) {
   std::set<mirror::Class*> classes;
   VisitClasses(GetClassesVisitor, &classes);
   for (mirror::Class* klass : classes) {
@@ -1274,7 +1252,7 @@
     // Check for circular dependencies between classes.
     if (!klass->IsResolved() && klass->GetClinitThreadId() == self->GetTid()) {
       ThrowClassCircularityError(klass);
-      klass->SetStatus(mirror::Class::kStatusError);
+      klass->SetStatus(mirror::Class::kStatusError, self);
       return NULL;
     }
     // Wait for the pending initialization to complete.
@@ -1383,26 +1361,26 @@
   return NULL;
 }
 
-mirror::Class* ClassLinker::DefineClass(const StringPiece& descriptor,
+mirror::Class* ClassLinker::DefineClass(const char* descriptor,
                                         mirror::ClassLoader* class_loader,
                                         const DexFile& dex_file,
                                         const DexFile::ClassDef& dex_class_def) {
   Thread* self = Thread::Current();
   SirtRef<mirror::Class> klass(self, NULL);
   // Load the class from the dex file.
-  if (!init_done_) {
+  if (UNLIKELY(!init_done_)) {
     // finish up init of hand crafted class_roots_
-    if (descriptor == "Ljava/lang/Object;") {
+    if (strcmp(descriptor, "Ljava/lang/Object;") == 0) {
       klass.reset(GetClassRoot(kJavaLangObject));
-    } else if (descriptor == "Ljava/lang/Class;") {
+    } else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {
       klass.reset(GetClassRoot(kJavaLangClass));
-    } else if (descriptor == "Ljava/lang/String;") {
+    } else if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
       klass.reset(GetClassRoot(kJavaLangString));
-    } else if (descriptor == "Ljava/lang/DexCache;") {
+    } else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) {
       klass.reset(GetClassRoot(kJavaLangDexCache));
-    } else if (descriptor == "Ljava/lang/reflect/ArtField;") {
+    } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtField;") == 0) {
       klass.reset(GetClassRoot(kJavaLangReflectArtField));
-    } else if (descriptor == "Ljava/lang/reflect/ArtMethod;") {
+    } else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) {
       klass.reset(GetClassRoot(kJavaLangReflectArtMethod));
     } else {
       klass.reset(AllocClass(self, SizeOfClass(dex_file, dex_class_def)));
@@ -1414,32 +1392,33 @@
   LoadClass(dex_file, dex_class_def, klass, class_loader);
   // Check for a pending exception during load
   if (self->IsExceptionPending()) {
-    klass->SetStatus(mirror::Class::kStatusError);
+    klass->SetStatus(mirror::Class::kStatusError, self);
     return NULL;
   }
   ObjectLock lock(self, klass.get());
   klass->SetClinitThreadId(self->GetTid());
-  // Add the newly loaded class to the loaded classes table.
-  SirtRef<mirror::Class> existing(self, InsertClass(descriptor, klass.get(), false));
-  if (existing.get() != NULL) {
-    // We failed to insert because we raced with another thread.
-    return EnsureResolved(self, existing.get());
+  {
+    // Add the newly loaded class to the loaded classes table.
+    mirror::Class* existing = InsertClass(descriptor, klass.get(), Hash(descriptor));
+    if (existing != NULL) {
+      // We failed to insert because we raced with another thread. Calling EnsureResolved may cause
+      // this thread to block.
+      return EnsureResolved(self, existing);
+    }
   }
   // Finish loading (if necessary) by finding parents
   CHECK(!klass->IsLoaded());
   if (!LoadSuperAndInterfaces(klass, dex_file)) {
     // Loading failed.
-    klass->SetStatus(mirror::Class::kStatusError);
-    lock.NotifyAll();
+    klass->SetStatus(mirror::Class::kStatusError, self);
     return NULL;
   }
   CHECK(klass->IsLoaded());
   // Link the class (if necessary)
   CHECK(!klass->IsResolved());
-  if (!LinkClass(klass, NULL)) {
+  if (!LinkClass(klass, NULL, self)) {
     // Linking failed.
-    klass->SetStatus(mirror::Class::kStatusError);
-    lock.NotifyAll();
+    klass->SetStatus(mirror::Class::kStatusError, self);
     return NULL;
   }
   CHECK(klass->IsResolved());
@@ -1734,7 +1713,7 @@
   klass->SetAccessFlags(access_flags);
   klass->SetClassLoader(class_loader);
   DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot);
-  klass->SetStatus(mirror::Class::kStatusIdx);
+  klass->SetStatus(mirror::Class::kStatusIdx, NULL);
 
   klass->SetDexTypeIndex(dex_class_def.class_idx_);
 
@@ -1966,11 +1945,13 @@
 mirror::Class* ClassLinker::InitializePrimitiveClass(mirror::Class* primitive_class, Primitive::Type type) {
   CHECK(primitive_class != NULL);
   // Must hold lock on object when initializing.
-  ObjectLock lock(Thread::Current(), primitive_class);
+  Thread* self = Thread::Current();
+  ObjectLock lock(self, primitive_class);
   primitive_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract);
   primitive_class->SetPrimitiveType(type);
-  primitive_class->SetStatus(mirror::Class::kStatusInitialized);
-  mirror::Class* existing = InsertClass(Primitive::Descriptor(type), primitive_class, false);
+  primitive_class->SetStatus(mirror::Class::kStatusInitialized, self);
+  const char* descriptor = Primitive::Descriptor(type);
+  mirror::Class* existing = InsertClass(descriptor, primitive_class, Hash(descriptor));
   CHECK(existing == NULL) << "InitPrimitiveClass(" << type << ") failed";
   return primitive_class;
 }
@@ -1988,12 +1969,11 @@
 // array class; that always comes from the base element class.
 //
 // Returns NULL with an exception raised on failure.
-mirror::Class* ClassLinker::CreateArrayClass(const std::string& descriptor,
+mirror::Class* ClassLinker::CreateArrayClass(const char* descriptor,
                                              mirror::ClassLoader* class_loader) {
-  CHECK_EQ('[', descriptor[0]);
-
   // Identify the underlying component type
-  mirror::Class* component_type = FindClass(descriptor.substr(1).c_str(), class_loader);
+  CHECK_EQ('[', descriptor[0]);
+  mirror::Class* component_type = FindClass(descriptor + 1, class_loader);
   if (component_type == NULL) {
     DCHECK(Thread::Current()->IsExceptionPending());
     return NULL;
@@ -2017,7 +1997,7 @@
   // class to the hash table --- necessary because of possible races with
   // other threads.)
   if (class_loader != component_type->GetClassLoader()) {
-    mirror::Class* new_class = LookupClass(descriptor.c_str(), component_type->GetClassLoader());
+    mirror::Class* new_class = LookupClass(descriptor, component_type->GetClassLoader());
     if (new_class != NULL) {
       return new_class;
     }
@@ -2033,21 +2013,23 @@
   // link step.
   Thread* self = Thread::Current();
   SirtRef<mirror::Class> new_class(self, NULL);
-  if (!init_done_) {
+  if (UNLIKELY(!init_done_)) {
     // Classes that were hand created, ie not by FindSystemClass
-    if (descriptor == "[Ljava/lang/Class;") {
+    if (strcmp(descriptor, "[Ljava/lang/Class;") == 0) {
       new_class.reset(GetClassRoot(kClassArrayClass));
-    } else if (descriptor == "[Ljava/lang/Object;") {
+    } else if (strcmp(descriptor, "[Ljava/lang/Object;") == 0) {
       new_class.reset(GetClassRoot(kObjectArrayClass));
-    } else if (descriptor == class_roots_descriptors_[kJavaLangStringArrayClass]) {
+    } else if (strcmp(descriptor, class_roots_descriptors_[kJavaLangStringArrayClass]) == 0) {
       new_class.reset(GetClassRoot(kJavaLangStringArrayClass));
-    } else if (descriptor == class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]) {
+    } else if (strcmp(descriptor,
+                      class_roots_descriptors_[kJavaLangReflectArtMethodArrayClass]) == 0) {
       new_class.reset(GetClassRoot(kJavaLangReflectArtMethodArrayClass));
-    } else if (descriptor == class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]) {
+    } else if (strcmp(descriptor,
+                      class_roots_descriptors_[kJavaLangReflectArtFieldArrayClass]) == 0) {
       new_class.reset(GetClassRoot(kJavaLangReflectArtFieldArrayClass));
-    } else if (descriptor == "[C") {
+    } else if (strcmp(descriptor, "[C") == 0) {
       new_class.reset(GetClassRoot(kCharArrayClass));
-    } else if (descriptor == "[I") {
+    } else if (strcmp(descriptor, "[I") == 0) {
       new_class.reset(GetClassRoot(kIntArrayClass));
     }
   }
@@ -2065,7 +2047,7 @@
   new_class->SetVTable(java_lang_Object->GetVTable());
   new_class->SetPrimitiveType(Primitive::kPrimNot);
   new_class->SetClassLoader(component_type->GetClassLoader());
-  new_class->SetStatus(mirror::Class::kStatusInitialized);
+  new_class->SetStatus(mirror::Class::kStatusInitialized, self);
   // don't need to set new_class->SetObjectSize(..)
   // because Object::SizeOf delegates to Array::SizeOf
 
@@ -2096,7 +2078,7 @@
 
   new_class->SetAccessFlags(access_flags);
 
-  mirror::Class* existing = InsertClass(descriptor, new_class.get(), false);
+  mirror::Class* existing = InsertClass(descriptor, new_class.get(), Hash(descriptor));
   if (existing == NULL) {
     return new_class.get();
   }
@@ -2137,8 +2119,8 @@
   return NULL;
 }
 
-mirror::Class* ClassLinker::InsertClass(const StringPiece& descriptor, mirror::Class* klass,
-                                        bool image_class) {
+mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* klass,
+                                        size_t hash) {
   if (VLOG_IS_ON(class_linker)) {
     mirror::DexCache* dex_cache = klass->GetDexCache();
     std::string source;
@@ -2148,21 +2130,22 @@
     }
     LOG(INFO) << "Loaded class " << descriptor << source;
   }
-  size_t hash = StringPieceHash()(descriptor);
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  Table& classes = image_class ? image_classes_ : classes_;
   mirror::Class* existing =
-      LookupClassLocked(descriptor.data(), klass->GetClassLoader(), hash, classes);
-#ifndef NDEBUG
-  // Check we don't have the class in the other table in error
-  Table& other_classes = image_class ? classes_ : image_classes_;
-  CHECK(LookupClassLocked(descriptor.data(), klass->GetClassLoader(), hash, other_classes) == NULL);
-#endif
+      LookupClassFromTableLocked(descriptor, klass->GetClassLoader(), hash);
   if (existing != NULL) {
     return existing;
   }
+  if (kIsDebugBuild && klass->GetClassLoader() == NULL && dex_cache_image_class_lookup_required_) {
+    // Check a class loaded with the system class loader matches one in the image if the class
+    // is in the image.
+    existing = LookupClassFromImage(descriptor);
+    if (existing != NULL) {
+      CHECK(klass == existing);
+    }
+  }
   Runtime::Current()->GetHeap()->VerifyObject(klass);
-  classes.insert(std::make_pair(hash, klass));
+  class_table_.insert(std::make_pair(hash, klass));
   Dirty();
   return NULL;
 }
@@ -2170,23 +2153,13 @@
 bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader) {
   size_t hash = Hash(descriptor);
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  // TODO: determine if its better to search classes_ or image_classes_ first
   ClassHelper kh;
-  for (auto it = classes_.lower_bound(hash), end = classes_.end(); it != end && it->first == hash;
+  for (auto it = class_table_.lower_bound(hash), end = class_table_.end(); it != end && it->first == hash;
        ++it) {
     mirror::Class* klass = it->second;
     kh.ChangeClass(klass);
     if (strcmp(kh.GetDescriptor(), descriptor) == 0 && klass->GetClassLoader() == class_loader) {
-      classes_.erase(it);
-      return true;
-    }
-  }
-  for (auto it = image_classes_.lower_bound(hash), end = classes_.end();
-      it != end && it->first == hash; ++it) {
-    mirror::Class* klass = it->second;
-    kh.ChangeClass(klass);
-    if (strcmp(kh.GetDescriptor(), descriptor) == 0 && klass->GetClassLoader() == class_loader) {
-      image_classes_.erase(it);
+      class_table_.erase(it);
       return true;
     }
   }
@@ -2196,64 +2169,153 @@
 mirror::Class* ClassLinker::LookupClass(const char* descriptor,
                                         const mirror::ClassLoader* class_loader) {
   size_t hash = Hash(descriptor);
-  ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  // TODO: determine if its better to search classes_ or image_classes_ first
-  mirror::Class* klass = NULL;
-  // Use image class only if the class_loader is null.
-  if (class_loader == NULL) {
-    klass = LookupClassLocked(descriptor, class_loader, hash, image_classes_);
+  {
+    ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+    mirror::Class* result = LookupClassFromTableLocked(descriptor, class_loader, hash);
+    if (result != NULL) {
+      return result;
+    }
   }
-  if (klass != NULL) {
-    return klass;
+  if (class_loader != NULL || !dex_cache_image_class_lookup_required_) {
+    return NULL;
+  } else {
+    // Lookup failed but need to search dex_caches_.
+    mirror::Class* result = LookupClassFromImage(descriptor);
+    if (result != NULL) {
+      InsertClass(descriptor, result, hash);
+    } else {
+      // Searching the image dex files/caches failed, we don't want to get into this situation
+      // often as map searches are faster, so after kMaxFailedDexCacheLookups move all image
+      // classes into the class table.
+      const int32_t kMaxFailedDexCacheLookups = 1000;
+      if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) {
+        MoveImageClassesToClassTable();
+      }
+    }
+    return result;
   }
-  return LookupClassLocked(descriptor, class_loader, hash, classes_);
 }
 
-mirror::Class* ClassLinker::LookupClassLocked(const char* descriptor,
-                                              const mirror::ClassLoader* class_loader,
-                                              size_t hash, const Table& classes) {
+mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor,
+                                                       const mirror::ClassLoader* class_loader,
+                                                       size_t hash) {
   ClassHelper kh(NULL, this);
-  auto end = classes_.end();
-  for (auto it = classes.lower_bound(hash); it != end && it->first == hash;
-      ++it) {
+  auto end = class_table_.end();
+  for (auto it = class_table_.lower_bound(hash); it != end && it->first == hash; ++it) {
     mirror::Class* klass = it->second;
     kh.ChangeClass(klass);
-    if (strcmp(descriptor, kh.GetDescriptor()) == 0 && klass->GetClassLoader() == class_loader) {
-#ifndef NDEBUG
-      for (++it; it != end && it->first == hash; ++it) {
-        mirror::Class* klass2 = it->second;
-        kh.ChangeClass(klass2);
-        CHECK(!(strcmp(descriptor, kh.GetDescriptor()) == 0 && klass2->GetClassLoader() == class_loader))
-                << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " "
-                << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader();
+    if (klass->GetClassLoader() == class_loader && strcmp(descriptor, kh.GetDescriptor()) == 0) {
+      if (kIsDebugBuild) {
+        // Check for duplicates in the table.
+        for (++it; it != end && it->first == hash; ++it) {
+          mirror::Class* klass2 = it->second;
+          kh.ChangeClass(klass2);
+          CHECK(!(strcmp(descriptor, kh.GetDescriptor()) == 0 && klass2->GetClassLoader() == class_loader))
+          << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " "
+          << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader();
+        }
       }
-#endif
       return klass;
     }
   }
   return NULL;
 }
 
-void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Class*>& classes) {
-  classes.clear();
-  size_t hash = Hash(descriptor);
-  ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  // TODO: determine if its better to search classes_ or image_classes_ first
+static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches()
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetImageSpace();
+  CHECK(image != NULL);
+  mirror::Object* root = image->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
+  return root->AsObjectArray<mirror::DexCache>();
+}
+
+void ClassLinker::MoveImageClassesToClassTable() {
+  Thread* self = Thread::Current();
+  WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+  if (!dex_cache_image_class_lookup_required_) {
+    return;  // All dex cache classes are already in the class table.
+  }
+  const char* old_no_suspend_cause =
+      self->StartAssertNoThreadSuspension("Moving image classes to class table");
+  mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches();
   ClassHelper kh(NULL, this);
-  for (auto it = classes_.lower_bound(hash), end = classes_.end(); it != end && it->first == hash;
-      ++it) {
-    mirror::Class* klass = it->second;
-    kh.ChangeClass(klass);
-    if (strcmp(descriptor, kh.GetDescriptor()) == 0) {
-      classes.push_back(klass);
+  for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+    mirror::DexCache* dex_cache = dex_caches->Get(i);
+    mirror::ObjectArray<mirror::Class>* types = dex_cache->GetResolvedTypes();
+    for (int32_t j = 0; j < types->GetLength(); j++) {
+      mirror::Class* klass = types->Get(j);
+      if (klass != NULL) {
+        kh.ChangeClass(klass);
+        DCHECK(klass->GetClassLoader() == NULL);
+        const char* descriptor = kh.GetDescriptor();
+        size_t hash = Hash(descriptor);
+        mirror::Class* existing = LookupClassFromTableLocked(descriptor, NULL, hash);
+        if (existing != NULL) {
+          CHECK(existing == klass) << PrettyClassAndClassLoader(existing) << " != "
+              << PrettyClassAndClassLoader(klass);
+        } else {
+          class_table_.insert(std::make_pair(hash, klass));
+        }
+      }
     }
   }
-  for (auto it = image_classes_.lower_bound(hash), end = classes_.end();
+  Dirty();
+  dex_cache_image_class_lookup_required_ = false;
+  self->EndAssertNoThreadSuspension(old_no_suspend_cause);
+}
+
+mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) {
+  Thread* self = Thread::Current();
+  const char* old_no_suspend_cause =
+      self->StartAssertNoThreadSuspension("Image class lookup");
+  mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches();
+  for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
+    mirror::DexCache* dex_cache = dex_caches->Get(i);
+    const DexFile* dex_file = dex_cache->GetDexFile();
+    // First search using the class def map, but don't bother for non-class types.
+    if (descriptor[0] == 'L') {
+      const DexFile::ClassDef* class_def = dex_file->FindClassDef(descriptor);
+      if (class_def != NULL) {
+        mirror::Class* klass = dex_cache->GetResolvedType(class_def->class_idx_);
+        if (klass != NULL) {
+          self->EndAssertNoThreadSuspension(old_no_suspend_cause);
+          return klass;
+        }
+      }
+    }
+    // Now try binary searching the string/type index.
+    const DexFile::StringId* string_id = dex_file->FindStringId(descriptor);
+    if (string_id != NULL) {
+      const DexFile::TypeId* type_id =
+          dex_file->FindTypeId(dex_file->GetIndexForStringId(*string_id));
+      if (type_id != NULL) {
+        uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id);
+        mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
+        if (klass != NULL) {
+          self->EndAssertNoThreadSuspension(old_no_suspend_cause);
+          return klass;
+        }
+      }
+    }
+  }
+  self->EndAssertNoThreadSuspension(old_no_suspend_cause);
+  return NULL;
+}
+
+void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Class*>& result) {
+  result.clear();
+  if (dex_cache_image_class_lookup_required_) {
+    MoveImageClassesToClassTable();
+  }
+  size_t hash = Hash(descriptor);
+  ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+  ClassHelper kh(NULL, this);
+  for (auto it = class_table_.lower_bound(hash), end = class_table_.end();
       it != end && it->first == hash; ++it) {
     mirror::Class* klass = it->second;
     kh.ChangeClass(klass);
     if (strcmp(descriptor, kh.GetDescriptor()) == 0) {
-      classes.push_back(klass);
+      result.push_back(klass);
     }
   }
 }
@@ -2277,12 +2339,12 @@
   }
 
   if (klass->GetStatus() == mirror::Class::kStatusResolved) {
-    klass->SetStatus(mirror::Class::kStatusVerifying);
+    klass->SetStatus(mirror::Class::kStatusVerifying, self);
   } else {
     CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime)
         << PrettyClass(klass);
     CHECK(!Runtime::Current()->IsCompiler());
-    klass->SetStatus(mirror::Class::kStatusVerifyingAtRuntime);
+    klass->SetStatus(mirror::Class::kStatusVerifyingAtRuntime, self);
   }
 
   // Verify super class.
@@ -2307,7 +2369,7 @@
       if (cause.get() != NULL) {
         self->GetException(NULL)->SetCause(cause.get());
       }
-      klass->SetStatus(mirror::Class::kStatusError);
+      klass->SetStatus(mirror::Class::kStatusError, self);
       return;
     }
   }
@@ -2322,7 +2384,7 @@
         << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
     ThrowVerifyError(klass, "Rejecting class %s because it failed compile-time verification",
                      PrettyDescriptor(klass).c_str());
-    klass->SetStatus(mirror::Class::kStatusError);
+    klass->SetStatus(mirror::Class::kStatusError, self);
     return;
   }
   verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
@@ -2344,10 +2406,10 @@
       // Even though there were no verifier failures we need to respect whether the super-class
       // was verified or requiring runtime reverification.
       if (super.get() == NULL || super->IsVerified()) {
-        klass->SetStatus(mirror::Class::kStatusVerified);
+        klass->SetStatus(mirror::Class::kStatusVerified, self);
       } else {
         CHECK_EQ(super->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
-        klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime);
+        klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime, self);
         // Pretend a soft failure occured so that we don't consider the class verified below.
         verifier_failure = verifier::MethodVerifier::kSoftFailure;
       }
@@ -2357,9 +2419,9 @@
       // failures at runtime will be handled by slow paths in the generated
       // code. Set status accordingly.
       if (Runtime::Current()->IsCompiler()) {
-        klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime);
+        klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime, self);
       } else {
-        klass->SetStatus(mirror::Class::kStatusVerified);
+        klass->SetStatus(mirror::Class::kStatusVerified, self);
       }
     }
   } else {
@@ -2368,7 +2430,7 @@
         << " because: " << error_msg;
     self->AssertNoPendingException();
     ThrowVerifyError(klass, "%s", error_msg.c_str());
-    klass->SetStatus(mirror::Class::kStatusError);
+    klass->SetStatus(mirror::Class::kStatusError, self);
   }
   if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) {
     // Class is verified so we don't need to do any access check on its methods.
@@ -2522,7 +2584,7 @@
   mirror::Class* proxy_class = GetClassRoot(kJavaLangReflectProxy);
   klass->SetDexCache(proxy_class->GetDexCache());
 
-  klass->SetStatus(mirror::Class::kStatusIdx);
+  klass->SetStatus(mirror::Class::kStatusIdx, self);
 
   klass->SetDexTypeIndex(DexFile::kDexNoIndex16);
 
@@ -2555,19 +2617,20 @@
   }
 
   klass->SetSuperClass(proxy_class);  // The super class is java.lang.reflect.Proxy
-  klass->SetStatus(mirror::Class::kStatusLoaded);  // Class is now effectively in the loaded state
+  klass->SetStatus(mirror::Class::kStatusLoaded, self);  // Class is now effectively in the loaded state
   self->AssertNoPendingException();
 
-  // Link the fields and virtual methods, creating vtable and iftables
-  if (!LinkClass(klass, interfaces)) {
-    klass->SetStatus(mirror::Class::kStatusError);
-    return NULL;
-  }
   {
-    ObjectLock lock(self, klass.get());  // Must hold lock on object when initializing.
+    ObjectLock lock(self, klass.get());  // Must hold lock on object when resolved.
+    // Link the fields and virtual methods, creating vtable and iftables
+    if (!LinkClass(klass, interfaces, self)) {
+      klass->SetStatus(mirror::Class::kStatusError, self);
+      return NULL;
+    }
+
     interfaces_sfield->SetObject(klass.get(), interfaces);
     throws_sfield->SetObject(klass.get(), throws);
-    klass->SetStatus(mirror::Class::kStatusInitialized);
+    klass->SetStatus(mirror::Class::kStatusInitialized, self);
   }
 
   // sanity checks
@@ -2806,7 +2869,7 @@
     }
 
     if (!ValidateSuperClassDescriptors(klass)) {
-      klass->SetStatus(mirror::Class::kStatusError);
+      klass->SetStatus(mirror::Class::kStatusError, self);
       return false;
     }
 
@@ -2815,7 +2878,7 @@
     // From here out other threads may observe that we're initializing and so changes of state
     // require the a notification.
     klass->SetClinitThreadId(self->GetTid());
-    klass->SetStatus(mirror::Class::kStatusInitializing);
+    klass->SetStatus(mirror::Class::kStatusInitializing, self);
 
     t0 = NanoTime();
   }
@@ -2837,8 +2900,7 @@
             << (self->GetException(NULL) != NULL ? self->GetException(NULL)->Dump() : "");
         ObjectLock lock(self, klass);
         // Initialization failed because the super-class is erroneous.
-        klass->SetStatus(mirror::Class::kStatusError);
-        lock.NotifyAll();
+        klass->SetStatus(mirror::Class::kStatusError, self);
         return false;
       }
     }
@@ -2884,7 +2946,7 @@
 
     if (self->IsExceptionPending()) {
       WrapExceptionInInitializer();
-      klass->SetStatus(mirror::Class::kStatusError);
+      klass->SetStatus(mirror::Class::kStatusError, self);
       success = false;
     } else {
       RuntimeStats* global_stats = Runtime::Current()->GetStats();
@@ -2894,13 +2956,12 @@
       global_stats->class_init_time_ns += (t1 - t0);
       thread_stats->class_init_time_ns += (t1 - t0);
       // Set the class as initialized except if failed to initialize static fields.
-      klass->SetStatus(mirror::Class::kStatusInitialized);
+      klass->SetStatus(mirror::Class::kStatusInitialized, self);
       if (VLOG_IS_ON(class_linker)) {
         ClassHelper kh(klass);
         LOG(INFO) << "Initialized class " << kh.GetDescriptor() << " from " << kh.GetLocation();
       }
     }
-    lock.NotifyAll();
   }
   return success;
 }
@@ -2917,7 +2978,7 @@
     // "interruptShouldThrow" was set), bail out.
     if (self->IsExceptionPending()) {
       WrapExceptionInInitializer();
-      klass->SetStatus(mirror::Class::kStatusError);
+      klass->SetStatus(mirror::Class::kStatusError, self);
       return false;
     }
     // Spurious wakeup? Go back to waiting.
@@ -3061,7 +3122,7 @@
 }
 
 bool ClassLinker::LinkClass(SirtRef<mirror::Class>& klass,
-                            mirror::ObjectArray<mirror::Class>* interfaces) {
+                            mirror::ObjectArray<mirror::Class>* interfaces, Thread* self) {
   CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus());
   if (!LinkSuperClass(klass)) {
     return false;
@@ -3078,7 +3139,7 @@
   CreateReferenceInstanceOffsets(klass);
   CreateReferenceStaticOffsets(klass);
   CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus());
-  klass->SetStatus(mirror::Class::kStatusResolved);
+  klass->SetStatus(mirror::Class::kStatusResolved, self);
   return true;
 }
 
@@ -3123,7 +3184,7 @@
     }
   }
   // Mark the class as loaded.
-  klass->SetStatus(mirror::Class::kStatusLoaded);
+  klass->SetStatus(mirror::Class::kStatusLoaded, NULL);
   return true;
 }
 
@@ -3175,13 +3236,13 @@
     return false;
   }
 
-#ifndef NDEBUG
-  // Ensure super classes are fully resolved prior to resolving fields..
-  while (super != NULL) {
-    CHECK(super->IsResolved());
-    super = super->GetSuperClass();
+  if (kIsDebugBuild) {
+    // Ensure super classes are fully resolved prior to resolving fields..
+    while (super != NULL) {
+      CHECK(super->IsResolved());
+      super = super->GetSuperClass();
+    }
   }
-#endif
   return true;
 }
 
@@ -3992,16 +4053,16 @@
   return dex_file.GetMethodShorty(method_id, length);
 }
 
-void ClassLinker::DumpAllClasses(int flags) const {
+void ClassLinker::DumpAllClasses(int flags) {
+  if (dex_cache_image_class_lookup_required_) {
+    MoveImageClassesToClassTable();
+  }
   // TODO: at the time this was written, it wasn't safe to call PrettyField with the ClassLinker
   // lock held, because it might need to resolve a field's type, which would try to take the lock.
   std::vector<mirror::Class*> all_classes;
   {
     ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-    for (const std::pair<size_t, mirror::Class*>& it : classes_) {
-      all_classes.push_back(it.second);
-    }
-    for (const std::pair<size_t, mirror::Class*>& it : image_classes_) {
+    for (const std::pair<size_t, mirror::Class*>& it : class_table_) {
       all_classes.push_back(it.second);
     }
   }
@@ -4011,15 +4072,20 @@
   }
 }
 
-void ClassLinker::DumpForSigQuit(std::ostream& os) const {
+void ClassLinker::DumpForSigQuit(std::ostream& os) {
+  if (dex_cache_image_class_lookup_required_) {
+    MoveImageClassesToClassTable();
+  }
   ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  os << "Loaded classes: " << image_classes_.size() << " image classes; "
-     << classes_.size() << " allocated classes\n";
+  os << "Loaded classes: " << class_table_.size() << " allocated classes\n";
 }
 
-size_t ClassLinker::NumLoadedClasses() const {
+size_t ClassLinker::NumLoadedClasses() {
+  if (dex_cache_image_class_lookup_required_) {
+    MoveImageClassesToClassTable();
+  }
   ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
-  return classes_.size() + image_classes_.size();
+  return class_table_.size();
 }
 
 pid_t ClassLinker::GetClassesLockOwner() {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 5ef6d8f..c5fb72c 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -73,7 +73,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Define a new a class based on a ClassDef from a DexFile
-  mirror::Class* DefineClass(const StringPiece& descriptor, mirror::ClassLoader* class_loader,
+  mirror::Class* DefineClass(const char* descriptor, mirror::ClassLoader* class_loader,
                              const DexFile& dex_file, const DexFile::ClassDef& dex_class_def)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -96,14 +96,17 @@
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void DumpAllClasses(int flags) const
+  void DumpAllClasses(int flags)
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void DumpForSigQuit(std::ostream& os) const
-      LOCKS_EXCLUDED(Locks::classlinker_classes_lock_);
+  void DumpForSigQuit(std::ostream& os)
+      LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  size_t NumLoadedClasses() const LOCKS_EXCLUDED(Locks::classlinker_classes_lock_);
+  size_t NumLoadedClasses()
+      LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Resolve a String with the given index from the DexFile, storing the
   // result in the DexCache. The referrer is used to identify the
@@ -219,12 +222,14 @@
     return boot_class_path_;
   }
 
-  void VisitClasses(ClassVisitor* visitor, void* arg) const
-      LOCKS_EXCLUDED(Locks::classlinker_classes_lock_);
+  void VisitClasses(ClassVisitor* visitor, void* arg)
+      LOCKS_EXCLUDED(dex_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   // Less efficient variant of VisitClasses that doesn't hold the classlinker_classes_lock_
   // when calling the visitor.
-  void VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) const
-      LOCKS_EXCLUDED(Locks::classlinker_classes_lock_);
+  void VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg)
+      LOCKS_EXCLUDED(dex_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void VisitRoots(RootVisitor* visitor, void* arg, bool clean_dirty)
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_, dex_lock_);
@@ -353,7 +358,7 @@
   // Attempts to insert a class into a class table.  Returns NULL if
   // the class was inserted, otherwise returns an existing class with
   // the same descriptor and ClassLoader.
-  mirror::Class* InsertClass(const StringPiece& descriptor, mirror::Class* klass, bool image_class)
+  mirror::Class* InsertClass(const char* descriptor, mirror::Class* klass, size_t hash)
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -394,7 +399,7 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 
-  mirror::Class* CreateArrayClass(const std::string& descriptor, mirror::ClassLoader* class_loader)
+  mirror::Class* CreateArrayClass(const char* descriptor, mirror::ClassLoader* class_loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void AppendToBootClassPath(const DexFile& dex_file)
@@ -453,7 +458,8 @@
                                                      const mirror::Class* klass2)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool LinkClass(SirtRef<mirror::Class>& klass, mirror::ObjectArray<mirror::Class>* interfaces)
+  bool LinkClass(SirtRef<mirror::Class>& klass, mirror::ObjectArray<mirror::Class>* interfaces,
+                 Thread* self)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool LinkSuperClass(SirtRef<mirror::Class>& klass)
@@ -530,12 +536,23 @@
   // mirror::Class* instances. Results should be compared for a matching
   // Class::descriptor_ and Class::class_loader_.
   typedef std::multimap<size_t, mirror::Class*> Table;
-  Table image_classes_ GUARDED_BY(Locks::classlinker_classes_lock_);
-  Table classes_ GUARDED_BY(Locks::classlinker_classes_lock_);
+  Table class_table_ GUARDED_BY(Locks::classlinker_classes_lock_);
 
-  mirror::Class* LookupClassLocked(const char* descriptor, const mirror::ClassLoader* class_loader,
-                                   size_t hash, const Table& classes)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::classlinker_classes_lock_);
+  // Do we need to search dex caches to find image classes?
+  bool dex_cache_image_class_lookup_required_;
+  // Number of times we've searched dex caches for a class. After a certain number of misses we move
+  // the classes into the class_table_ to avoid dex cache based searches.
+  AtomicInteger failed_dex_cache_class_lookups_;
+
+  mirror::Class* LookupClassFromTableLocked(const char* descriptor,
+                                            const mirror::ClassLoader* class_loader,
+                                            size_t hash)
+      SHARED_LOCKS_REQUIRED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+
+  void MoveImageClassesToClassTable() LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  mirror::Class* LookupClassFromImage(const char* descriptor)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // indexes into class_roots_.
   // needs to be kept in sync with class_roots_descriptors_.
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index 09fa19e..6e8736a 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -529,7 +529,20 @@
     case k30t:  os << StringPrintf("%s %+d", opcode, VRegA_30t()); break;
     case k31t:  os << StringPrintf("%s v%d, %+d", opcode, VRegA_31t(), VRegB_31t()); break;
     case k31i:  os << StringPrintf("%s v%d, #%+d", opcode, VRegA_31i(), VRegB_31i()); break;
-    case k31c:  os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_31c(), VRegB_31c()); break;
+    case k31c:
+      if (Opcode() == CONST_STRING_JUMBO) {
+        uint32_t string_idx = VRegB_31c();
+        if (file != NULL) {
+          os << StringPrintf("%s v%d, %s // string@%d", opcode, VRegA_31c(),
+                             PrintableString(file->StringDataByIdx(string_idx)).c_str(),
+                             string_idx);
+        } else {
+          os << StringPrintf("%s v%d, string@%d", opcode, VRegA_31c(), string_idx);
+        }
+      } else {
+        os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_31c(), VRegB_31c()); break;
+      }
+      break;
     case k35c: {
       uint32_t arg[5];
       GetArgs(arg);
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index d7398ca..e3a75cf 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -16,6 +16,10 @@
 
 #include "intern_table.h"
 
+#include "gc/space/image_space.h"
+#include "mirror/dex_cache.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/object-inl.h"
 #include "mirror/string.h"
 #include "thread.h"
 #include "UniquePtr.h"
@@ -23,8 +27,8 @@
 
 namespace art {
 
-InternTable::InternTable() : intern_table_lock_("InternTable lock"), is_dirty_(false) {
-}
+InternTable::InternTable()
+    : intern_table_lock_("InternTable lock"), is_dirty_(false) {}
 
 size_t InternTable::Size() const {
   MutexLock mu(Thread::Current(), intern_table_lock_);
@@ -34,11 +38,11 @@
 void InternTable::DumpForSigQuit(std::ostream& os) const {
   MutexLock mu(Thread::Current(), intern_table_lock_);
   os << "Intern table: " << strong_interns_.size() << " strong; "
-     << weak_interns_.size() << " weak; "
-     << image_strong_interns_.size() << " image strong\n";
+     << weak_interns_.size() << " weak\n";
 }
 
-void InternTable::VisitRoots(RootVisitor* visitor, void* arg, bool clean_dirty) {
+void InternTable::VisitRoots(RootVisitor* visitor, void* arg,
+                             bool clean_dirty) {
   MutexLock mu(Thread::Current(), intern_table_lock_);
   for (const auto& strong_intern : strong_interns_) {
     visitor(strong_intern.second, arg);
@@ -46,10 +50,12 @@
   if (clean_dirty) {
     is_dirty_ = false;
   }
-  // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots.
+  // Note: we deliberately don't visit the weak_interns_ table and the immutable
+  // image roots.
 }
 
-mirror::String* InternTable::Lookup(Table& table, mirror::String* s, uint32_t hash_code) {
+mirror::String* InternTable::Lookup(Table& table, mirror::String* s,
+                                    uint32_t hash_code) {
   intern_table_lock_.AssertHeld(Thread::Current());
   for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) {
     mirror::String* existing_string = it->second;
@@ -60,18 +66,15 @@
   return NULL;
 }
 
-mirror::String* InternTable::Insert(Table& table, mirror::String* s, uint32_t hash_code) {
+mirror::String* InternTable::Insert(Table& table, mirror::String* s,
+                                    uint32_t hash_code) {
   intern_table_lock_.AssertHeld(Thread::Current());
   table.insert(std::make_pair(hash_code, s));
   return s;
 }
 
-void InternTable::RegisterStrong(mirror::String* s) {
-  MutexLock mu(Thread::Current(), intern_table_lock_);
-  Insert(image_strong_interns_, s, s->GetHashCode());
-}
-
-void InternTable::Remove(Table& table, const mirror::String* s, uint32_t hash_code) {
+void InternTable::Remove(Table& table, const mirror::String* s,
+                         uint32_t hash_code) {
   intern_table_lock_.AssertHeld(Thread::Current());
   for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) {
     if (it->second == s) {
@@ -81,6 +84,31 @@
   }
 }
 
+static mirror::String* LookupStringFromImage(mirror::String* s)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetImageSpace();
+  if (image == NULL) {
+    return NULL;  // No image present.
+  }
+  mirror::Object* root = image->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
+  mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>();
+  const std::string utf8 = s->ToModifiedUtf8();
+  for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
+    mirror::DexCache* dex_cache = dex_caches->Get(i);
+    const DexFile* dex_file = dex_cache->GetDexFile();
+    // Binary search the dex file for the string index.
+    const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str());
+    if (string_id != NULL) {
+      uint32_t string_idx = dex_file->GetIndexForStringId(*string_id);
+      mirror::String* image = dex_cache->GetResolvedString(string_idx);
+      if (image != NULL) {
+        return image;
+      }
+    }
+  }
+  return NULL;
+}
+
 mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) {
   MutexLock mu(Thread::Current(), intern_table_lock_);
 
@@ -93,15 +121,16 @@
     if (strong != NULL) {
       return strong;
     }
-    // Check the image table for a match.
-    mirror::String* image = Lookup(image_strong_interns_, s, hash_code);
-    if (image != NULL) {
-      return image;
-    }
 
     // Mark as dirty so that we rescan the roots.
     Dirty();
 
+    // Check the image for a match.
+    mirror::String* image = LookupStringFromImage(s);
+    if (image != NULL) {
+      return Insert(strong_interns_, image, hash_code);
+    }
+
     // There is no match in the strong table, check the weak table.
     mirror::String* weak = Lookup(weak_interns_, s, hash_code);
     if (weak != NULL) {
@@ -110,7 +139,8 @@
       return Insert(strong_interns_, weak, hash_code);
     }
 
-    // No match in the strong table or the weak table. Insert into the strong table.
+    // No match in the strong table or the weak table. Insert into the strong
+    // table.
     return Insert(strong_interns_, s, hash_code);
   }
 
@@ -119,10 +149,10 @@
   if (strong != NULL) {
     return strong;
   }
-  // Check the image table for a match.
-  mirror::String* image = Lookup(image_strong_interns_, s, hash_code);
+  // Check the image for a match.
+  mirror::String* image = LookupStringFromImage(s);
   if (image != NULL) {
-    return image;
+    return Insert(weak_interns_, image, hash_code);
   }
   // Check the weak table for a match.
   mirror::String* weak = Lookup(weak_interns_, s, hash_code);
@@ -133,12 +163,15 @@
   return Insert(weak_interns_, s, hash_code);
 }
 
-mirror::String* InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) {
-  return InternStrong(mirror::String::AllocFromModifiedUtf8(Thread::Current(), utf16_length, utf8_data));
+mirror::String* InternTable::InternStrong(int32_t utf16_length,
+                                          const char* utf8_data) {
+  return InternStrong(mirror::String::AllocFromModifiedUtf8(
+      Thread::Current(), utf16_length, utf8_data));
 }
 
 mirror::String* InternTable::InternStrong(const char* utf8_data) {
-  return InternStrong(mirror::String::AllocFromModifiedUtf8(Thread::Current(), utf8_data));
+  return InternStrong(
+      mirror::String::AllocFromModifiedUtf8(Thread::Current(), utf8_data));
 }
 
 mirror::String* InternTable::InternStrong(mirror::String* s) {
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index 5031ce3..a804d1f 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -55,10 +55,6 @@
   // Interns a potentially new string in the 'weak' table. (See above.)
   mirror::String* InternWeak(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Register a String trusting that it is safe to intern.
-  // Used when reinitializing InternTable from an image.
-  void RegisterStrong(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   void SweepInternTableWeaks(IsMarkedTester is_marked, void* arg)
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
@@ -88,7 +84,6 @@
 
   mutable Mutex intern_table_lock_;
   bool is_dirty_;
-  Table image_strong_interns_ GUARDED_BY(intern_table_lock_);
   Table strong_interns_ GUARDED_BY(intern_table_lock_);
   Table weak_interns_ GUARDED_BY(intern_table_lock_);
 };
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 19e134f..5e8b827 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -50,24 +50,26 @@
   java_lang_Class_ = NULL;
 }
 
-void Class::SetStatus(Status new_status) {
-  if (UNLIKELY(new_status <= GetStatus() && new_status != kStatusError)) {
-    bool class_linker_initialized = Runtime::Current()->GetClassLinker() != nullptr;
-    if (class_linker_initialized) {
+void Class::SetStatus(Status new_status, Thread* self) {
+  Status old_status = GetStatus();
+  bool class_linker_initialized = Runtime::Current()->GetClassLinker() != nullptr;
+  if (LIKELY(class_linker_initialized)) {
+    if (UNLIKELY(new_status <= old_status && new_status != kStatusError)) {
       LOG(FATAL) << "Unexpected change back of class status for " << PrettyClass(this) << " "
-          << GetStatus() << " -> " << new_status;
+          << old_status << " -> " << new_status;
     }
-  }
-  if (new_status > kStatusResolved) {
-    CHECK_EQ(GetThinLockId(), Thread::Current()->GetThinLockId())
-        << "Attempt to change status of class while not holding its lock " << PrettyClass(this);
+    if (new_status >= kStatusResolved || old_status >= kStatusResolved) {
+      // When classes are being resolved the resolution code should hold the lock.
+      CHECK_EQ(GetThinLockId(), self->GetThinLockId())
+            << "Attempt to change status of class while not holding its lock: "
+            << PrettyClass(this) << " " << old_status << " -> " << new_status;
+    }
   }
   if (new_status == kStatusError) {
     CHECK_NE(GetStatus(), kStatusError)
         << "Attempt to set as erroneous an already erroneous class " << PrettyClass(this);
 
     // Stash current exception.
-    Thread* self = Thread::Current();
     SirtRef<mirror::Object> old_throw_this_object(self, NULL);
     SirtRef<mirror::ArtMethod> old_throw_method(self, NULL);
     SirtRef<mirror::Throwable> old_exception(self, NULL);
@@ -103,7 +105,13 @@
     self->SetException(gc_safe_throw_location, old_exception.get());
   }
   CHECK(sizeof(Status) == sizeof(uint32_t)) << PrettyClass(this);
-  return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
+  SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
+  // Classes that are being resolved or initialized need to notify waiters that the class status
+  // changed. See ClassLinker::EnsureResolved and ClassLinker::WaitForInitializeClass.
+  if ((old_status >= kStatusResolved || new_status >= kStatusResolved) &&
+      class_linker_initialized) {
+    NotifyAll(self);
+  }
 }
 
 void Class::SetDexCache(DexCache* new_dex_cache) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 638b67f..99f3850 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -125,10 +125,10 @@
 
   Status GetStatus() const {
     DCHECK_EQ(sizeof(Status), sizeof(uint32_t));
-    return static_cast<Status>(GetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), false));
+    return static_cast<Status>(GetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), true));
   }
 
-  void SetStatus(Status new_status) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void SetStatus(Status new_status, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Returns true if the class has failed to link.
   bool IsErroneous() const {
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index ff193c9..66c51e6 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -792,6 +792,8 @@
       return;
     }
     // no-op;  there are no waiters to notify.
+    // We inflate here in case the Notify is in a tight loop. Without inflation here the waiter
+    // will struggle to get in. Bug 6961405.
     Inflate(self, obj);
   } else {
     // It's a fat lock.
@@ -811,6 +813,8 @@
       return;
     }
     // no-op;  there are no waiters to notify.
+    // We inflate here in case the NotifyAll is in a tight loop. Without inflation here the waiter
+    // will struggle to get in. Bug 6961405.
     Inflate(self, obj);
   } else {
     // It's a fat lock.
@@ -948,6 +952,27 @@
   }
 }
 
+bool Monitor::IsValidLockWord(int32_t lock_word) {
+  if (lock_word == 0) {
+    return true;
+  } else if (LW_SHAPE(lock_word) == LW_SHAPE_FAT) {
+    Monitor* mon = LW_MONITOR(lock_word);
+    MonitorList* list = Runtime::Current()->GetMonitorList();
+    MutexLock mu(Thread::Current(), list->monitor_list_lock_);
+    bool found = false;
+    for (Monitor* list_mon : list->list_) {
+      if (mon == list_mon) {
+        found = true;
+        break;
+      }
+    }
+    return found;
+  } else {
+    // TODO: thin lock validity checking.
+    return LW_SHAPE(lock_word) == LW_SHAPE_THIN;
+  }
+}
+
 void Monitor::TranslateLocation(const mirror::ArtMethod* method, uint32_t dex_pc,
                                 const char*& source_file, uint32_t& line_number) const {
   // If method is null, location is unknown
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 02c10a7..6651768 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -100,6 +100,8 @@
                          void* callback_context)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  static bool IsValidLockWord(int32_t lock_word);
+
   mirror::Object* GetObject();
 
  private:
@@ -188,6 +190,7 @@
   Mutex monitor_list_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   std::list<Monitor*> list_ GUARDED_BY(monitor_list_lock_);
 
+  friend class Monitor;
   DISALLOW_COPY_AND_ASSIGN(MonitorList);
 };
 
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 4ee3533..2f4e427 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -158,7 +158,8 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   class_linker->RegisterDexFile(*dex_file);
   mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(javaLoader);
-  mirror::Class* result = class_linker->DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
+  mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file,
+                                                    *dex_class_def);
   VLOG(class_linker) << "DexFile_defineClassNative returning " << result;
   return soa.AddLocalReference<jclass>(result);
 }
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index e3ec3bc..ae45701 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -37,6 +37,7 @@
   std::vector<std::string> features;
   features.push_back("method-trace-profiling");
   features.push_back("method-trace-profiling-streaming");
+  features.push_back("method-sample-profiling");
   features.push_back("hprof-heap-dump");
   features.push_back("hprof-heap-dump-streaming");
   return toStringArray(env, features);
@@ -58,8 +59,9 @@
   Runtime::Current()->ResetStats(kinds);
 }
 
-static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags) {
-  Trace::Start("[DDMS]", -1, bufferSize, flags, true);
+static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags,
+                                               jboolean samplingEnabled, jint intervalUs) {
+  Trace::Start("[DDMS]", -1, bufferSize, flags, true, samplingEnabled, intervalUs);
 }
 
 static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename,
@@ -82,7 +84,7 @@
   if (traceFilename.c_str() == NULL) {
     return;
   }
-  Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, false);
+  Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, false, false, 0);
 }
 
 static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename,
@@ -91,7 +93,7 @@
   if (traceFilename.c_str() == NULL) {
     return;
   }
-  Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, false);
+  Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, false, false, 0);
 }
 
 static jboolean VMDebug_isMethodTracingActive(JNIEnv*, jclass) {
@@ -151,7 +153,8 @@
   return Runtime::Current()->GetClassLinker()->DumpAllClasses(flags);
 }
 
-static jint VMDebug_getLoadedClassCount(JNIEnv*, jclass) {
+static jint VMDebug_getLoadedClassCount(JNIEnv* env, jclass) {
+  ScopedObjectAccess soa(env);
   return Runtime::Current()->GetClassLinker()->NumLoadedClasses();
 }
 
@@ -322,7 +325,7 @@
   NATIVE_METHOD(VMDebug, startAllocCounting, "()V"),
   NATIVE_METHOD(VMDebug, startEmulatorTracing, "()V"),
   NATIVE_METHOD(VMDebug, startInstructionCounting, "()V"),
-  NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(II)V"),
+  NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"),
   NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V"),
   NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;II)V"),
   NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"),
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 51a67c1..5679d4e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -925,7 +925,8 @@
   method_trace_file_size_ = options->method_trace_file_size_;
 
   if (options->method_trace_) {
-    Trace::Start(options->method_trace_file_.c_str(), -1, options->method_trace_file_size_, 0, false);
+    Trace::Start(options->method_trace_file_.c_str(), -1, options->method_trace_file_size_, 0,
+                 false, false, 0);
   }
 
   VLOG(startup) << "Runtime::Init exiting";
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 4c092fd..6d040e1 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -122,9 +122,6 @@
 #endif
 
 Trace* volatile Trace::the_trace_ = NULL;
-// TODO: Add way to enable sampling and set interval through gui.
-bool Trace::sampling_enabled_ = true;
-uint32_t Trace::sampling_interval_us_ = 1000;
 pthread_t Trace::sampling_pthread_ = 0U;
 UniquePtr<std::vector<mirror::ArtMethod*> > Trace::temp_stack_trace_;
 
@@ -301,11 +298,12 @@
 
 void* Trace::RunSamplingThread(void* arg) {
   Runtime* runtime = Runtime::Current();
+  int interval_us = reinterpret_cast<int>(arg);
   CHECK(runtime->AttachCurrentThread("Sampling Profiler", true, runtime->GetSystemThreadGroup(),
                                      !runtime->IsCompiler()));
 
   while (true) {
-    usleep(sampling_interval_us_);
+    usleep(interval_us);
     ATRACE_BEGIN("Profile sampling");
     Thread* self = Thread::Current();
     Trace* the_trace;
@@ -331,7 +329,7 @@
 }
 
 void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int flags,
-                  bool direct_to_ddms) {
+                  bool direct_to_ddms, bool sampling_enabled, int interval_us) {
   Thread* self = Thread::Current();
   {
     MutexLock mu(self, *Locks::trace_lock_);
@@ -367,16 +365,19 @@
     if (the_trace_ != NULL) {
       LOG(ERROR) << "Trace already in progress, ignoring this request";
     } else {
-      the_trace_ = new Trace(trace_file.release(), buffer_size, flags);
+      the_trace_ = new Trace(trace_file.release(), buffer_size, flags, sampling_enabled);
 
       // Enable count of allocs if specified in the flags.
       if ((flags && kTraceCountAllocs) != 0) {
         runtime->SetStatsEnabled(true);
       }
 
-      if (sampling_enabled_) {
-        CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, NULL, &RunSamplingThread, NULL),
-                           "Sampling profiler thread");
+
+
+      if (sampling_enabled) {
+        CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, NULL, &RunSamplingThread,
+                                            reinterpret_cast<void*>(interval_us)),
+                                            "Sampling profiler thread");
       } else {
         runtime->GetInstrumentation()->AddListener(the_trace_,
                                                    instrumentation::Instrumentation::kMethodEntered |
@@ -407,7 +408,7 @@
   if (the_trace != NULL) {
     the_trace->FinishTracing();
 
-    if (sampling_enabled_) {
+    if (the_trace->sampling_enabled_) {
       MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
       runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, NULL);
     } else {
@@ -420,7 +421,7 @@
   }
   runtime->GetThreadList()->ResumeAll();
 
-  if (sampling_enabled_ && sampling_pthread != 0U) {
+  if (sampling_pthread != 0U) {
     CHECK_PTHREAD_CALL(pthread_join, (sampling_pthread, NULL), "sampling thread shutdown");
   }
 }
@@ -436,10 +437,10 @@
   return the_trace_ != NULL;
 }
 
-Trace::Trace(File* trace_file, int buffer_size, int flags)
+Trace::Trace(File* trace_file, int buffer_size, int flags, bool sampling_enabled)
     : trace_file_(trace_file), buf_(new uint8_t[buffer_size]()), flags_(flags),
-      clock_source_(default_clock_source_), buffer_size_(buffer_size), start_time_(MicroTime()),
-      cur_offset_(0),  overflow_(false) {
+      sampling_enabled_(sampling_enabled), clock_source_(default_clock_source_),
+      buffer_size_(buffer_size), start_time_(MicroTime()), cur_offset_(0),  overflow_(false) {
   // Set up the beginning of the trace.
   uint16_t trace_version = GetTraceVersion(clock_source_);
   memset(buf_.get(), 0, kTraceHeaderLength);
@@ -456,10 +457,6 @@
   cur_offset_ = kTraceHeaderLength;
 }
 
-Trace::~Trace() {
-  CHECK_EQ(sampling_pthread_, static_cast<pthread_t>(0U));
-}
-
 static void DumpBuf(uint8_t* buf, size_t buf_size, ProfilerClockSource clock_source)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   uint8_t* ptr = buf + kTraceHeaderLength;
diff --git a/runtime/trace.h b/runtime/trace.h
index 6fc3790..06cb6a6 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -51,7 +51,7 @@
   static void SetDefaultClockSource(ProfilerClockSource clock_source);
 
   static void Start(const char* trace_filename, int trace_fd, int buffer_size, int flags,
-                    bool direct_to_ddms)
+                    bool direct_to_ddms, bool sampling_enabled, int interval_us)
   LOCKS_EXCLUDED(Locks::mutator_lock_,
                  Locks::thread_list_lock_,
                  Locks::thread_suspend_count_lock_,
@@ -88,11 +88,10 @@
   // Clear and store an old stack trace for later use.
   static void FreeStackTrace(std::vector<mirror::ArtMethod*>* stack_trace);
 
-  ~Trace();
-
  private:
-  explicit Trace(File* trace_file, int buffer_size, int flags);
+  explicit Trace(File* trace_file, int buffer_size, int flags, bool sampling_enabled);
 
+  // The sampling interval in microseconds is passed as an argument.
   static void* RunSamplingThread(void* arg) LOCKS_EXCLUDED(Locks::trace_lock_);
 
   void FinishTracing() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -115,12 +114,6 @@
   // The default profiler clock source.
   static ProfilerClockSource default_clock_source_;
 
-  // True if traceview should sample instead of instrumenting method entry/exit.
-  static bool sampling_enabled_;
-
-  // Sampling interval in microseconds.
-  static uint32_t sampling_interval_us_;
-
   // Sampling thread, non-zero when sampling.
   static pthread_t sampling_pthread_;
 
@@ -136,6 +129,9 @@
   // Flags enabling extra tracing of things such as alloc counts.
   const int flags_;
 
+  // True if traceview should sample instead of instrumenting method entry/exit.
+  const bool sampling_enabled_;
+
   const ProfilerClockSource clock_source_;
 
   // Size of buf_.
diff --git a/test/016-intern/expected.txt b/test/016-intern/expected.txt
index 7d91963..0b7ac3d 100644
--- a/test/016-intern/expected.txt
+++ b/test/016-intern/expected.txt
@@ -1 +1,3 @@
 good! foobar
+good! foo
+good! null
diff --git a/test/016-intern/src/Main.java b/test/016-intern/src/Main.java
index 4306863..01cbf18 100644
--- a/test/016-intern/src/Main.java
+++ b/test/016-intern/src/Main.java
@@ -20,15 +20,33 @@
 public class Main {
     public static void main(String args[]) {
         String a, b;
-        String foo = "foo";
-        String bar = "bar";
+        final String foo = "foo";
+        final String bar = "bar";
 
+        // Two interned strings should match.
         a = foo.concat(bar).intern();
         b = foo.concat(bar).intern();
         if (a == b && foo != bar) {
             System.out.println("good! " + a);
         } else {
-            System.out.println("bad!");
+            System.out.println("bad! " + a + " != " + b);
+        }
+
+        // An interned string should match a string literal.
+        a = ("f" + foo.substring(1,3)).intern();
+        if (a == foo) {
+            System.out.println("good! " + a);
+        } else {
+            System.out.println("bad! " + a + " != " + b);
+        }
+
+        // Check that a string literal in libcore equals one in the app.
+        a = (new java.nio.charset.IllegalCharsetNameException(null)).getMessage();
+        b = "null";
+        if (a == b) {
+            System.out.println("good! " + a);
+        } else {
+            System.out.println("bad! " + a + " != " + b);
         }
     }
 }