More space refactoring.

Add common interface, AllocSpace.

Renamed the old AllocSpace to DLMallocSpace.

Added an new option enforce_target_size_, which when enabled, doesn't let
the heap grow past ideal heap size calculated during last Gc.

Removed redundant AllocationSize calls.

Moved large object space to its own file instead of being in space.h/cc.

Change-Id: I15e60531114bf213800599737cbd66ef44b46b15
diff --git a/src/gc/large_object_space.cc b/src/gc/large_object_space.cc
new file mode 100644
index 0000000..72c6c73
--- /dev/null
+++ b/src/gc/large_object_space.cc
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "large_object_space.h"
+#include "UniquePtr.h"
+#include "dlmalloc.h"
+#include "file.h"
+#include "image.h"
+#include "logging.h"
+#include "os.h"
+#include "space_bitmap.h"
+#include "stl_util.h"
+#include "utils.h"
+
+namespace art {
+
+void LargeObjectSpace::SwapBitmaps() {
+  SpaceSetMap* temp_live_objects = live_objects_.release();
+  live_objects_.reset(mark_objects_.release());
+  mark_objects_.reset(temp_live_objects);
+  // Swap names to get more descriptive diagnostics.
+  std::string temp_name = live_objects_->GetName();
+  live_objects_->SetName(mark_objects_->GetName());
+  mark_objects_->SetName(temp_name);
+}
+
+LargeObjectSpace::LargeObjectSpace(const std::string& name)
+    : DiscontinuousSpace(name, kGcRetentionPolicyAlwaysCollect),
+      num_bytes_allocated_(0), num_objects_allocated_(0), total_bytes_allocated_(0),
+      total_objects_allocated_(0) {
+  live_objects_.reset(new SpaceSetMap("large live objects"));
+  mark_objects_.reset(new SpaceSetMap("large marked objects"));
+}
+
+
+void LargeObjectSpace::CopyLiveToMarked() {
+  mark_objects_->CopyFrom(*live_objects_.get());
+}
+
+LargeObjectMapSpace::LargeObjectMapSpace(const std::string& name)
+    : LargeObjectSpace(name),
+      lock_("large object space lock", kAllocSpaceLock)
+{
+
+}
+
+LargeObjectMapSpace* LargeObjectMapSpace::Create(const std::string& name) {
+  return new LargeObjectMapSpace(name);
+}
+
+Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes) {
+  MemMap* mem_map = MemMap::MapAnonymous("allocation", NULL, num_bytes, PROT_READ | PROT_WRITE);
+  if (mem_map == NULL) {
+    return NULL;
+  }
+  MutexLock mu(self, lock_);
+  Object* obj = reinterpret_cast<Object*>(mem_map->Begin());
+  large_objects_.push_back(obj);
+  mem_maps_.Put(obj, mem_map);
+  size_t allocation_size = mem_map->Size();
+  num_bytes_allocated_ += allocation_size;
+  total_bytes_allocated_ += allocation_size;
+  ++num_objects_allocated_;
+  ++total_objects_allocated_;
+  return obj;
+}
+
+size_t LargeObjectMapSpace::Free(Thread* self, Object* ptr) {
+  MutexLock mu(self, lock_);
+  MemMaps::iterator found = mem_maps_.find(ptr);
+  CHECK(found != mem_maps_.end()) << "Attempted to free large object which was not live";
+  DCHECK_GE(num_bytes_allocated_, found->second->Size());
+  size_t allocation_size = found->second->Size();
+  num_bytes_allocated_ -= allocation_size;
+  --num_objects_allocated_;
+  delete found->second;
+  mem_maps_.erase(found);
+  return allocation_size;
+}
+
+size_t LargeObjectMapSpace::AllocationSize(const Object* obj) {
+  MutexLock mu(Thread::Current(), lock_);
+  MemMaps::iterator found = mem_maps_.find(const_cast<Object*>(obj));
+  CHECK(found != mem_maps_.end()) << "Attempted to get size of a large object which is not live";
+  return found->second->Size();
+}
+
+size_t LargeObjectSpace::FreeList(Thread* self, size_t num_ptrs, Object** ptrs) {
+  size_t total = 0;
+  for (size_t i = 0; i < num_ptrs; ++i) {
+    if (kDebugSpaces) {
+      CHECK(Contains(ptrs[i]));
+    }
+    total += Free(self, ptrs[i]);
+  }
+  return total;
+}
+
+void LargeObjectMapSpace::Walk(DlMallocSpace::WalkCallback callback, void* arg) {
+  MutexLock mu(Thread::Current(), lock_);
+  for (MemMaps::iterator it = mem_maps_.begin(); it != mem_maps_.end(); ++it) {
+    MemMap* mem_map = it->second;
+    callback(mem_map->Begin(), mem_map->End(), mem_map->Size(), arg);
+    callback(NULL, NULL, 0, arg);
+  }
+}
+
+bool LargeObjectMapSpace::Contains(const Object* obj) const {
+  MutexLock mu(Thread::Current(), lock_);
+  return mem_maps_.find(const_cast<Object*>(obj)) != mem_maps_.end();
+}
+
+FreeListSpace* FreeListSpace::Create(const std::string& name, byte* requested_begin, size_t size) {
+  CHECK(size % kAlignment == 0);
+  MemMap* mem_map = MemMap::MapAnonymous(name.c_str(), requested_begin, size,
+                                         PROT_READ | PROT_WRITE);
+  CHECK(mem_map != NULL) << "Failed to allocate large object space mem map";
+  return new FreeListSpace(name, mem_map, mem_map->Begin(), mem_map->End());
+}
+
+FreeListSpace::FreeListSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end)
+    : LargeObjectSpace(name),
+      begin_(begin),
+      end_(end),
+      mem_map_(mem_map),
+      lock_("free list space lock", kAllocSpaceLock) {
+  chunks_.resize(Size() / kAlignment + 1);
+  // Add a dummy chunk so we don't need to handle chunks having no next chunk.
+  chunks_.back().SetSize(kAlignment, false);
+  // Start out with one large free chunk.
+  AddFreeChunk(begin_, end_ - begin_, NULL);
+}
+
+FreeListSpace::~FreeListSpace() {
+
+}
+
+void FreeListSpace::AddFreeChunk(void* address, size_t size, Chunk* previous) {
+  Chunk* chunk = ChunkFromAddr(address);
+  chunk->SetSize(size, true);
+  chunk->SetPrevious(previous);
+  Chunk* next_chunk = GetNextChunk(chunk);
+  next_chunk->SetPrevious(chunk);
+  free_chunks_.insert(chunk);
+}
+
+FreeListSpace::Chunk* FreeListSpace::ChunkFromAddr(void* address) {
+  size_t offset = reinterpret_cast<byte*>(address) - Begin();
+  DCHECK(IsAligned<kAlignment>(offset));
+  DCHECK_LT(offset, Size());
+  return &chunks_[offset / kAlignment];
+}
+
+void* FreeListSpace::AddrFromChunk(Chunk* chunk) {
+  return reinterpret_cast<void*>(Begin() + (chunk - &chunks_.front()) * kAlignment);
+}
+
+void FreeListSpace::RemoveFreeChunk(Chunk* chunk) {
+  // TODO: C++0x
+  // TODO: Improve performance, this might be slow.
+  std::pair<FreeChunks::iterator, FreeChunks::iterator> range = free_chunks_.equal_range(chunk);
+  for (FreeChunks::iterator it = range.first; it != range.second; ++it) {
+    if (*it == chunk) {
+      free_chunks_.erase(it);
+      return;
+    }
+  }
+}
+
+void FreeListSpace::Walk(DlMallocSpace::WalkCallback callback, void* arg) {
+  MutexLock mu(Thread::Current(), lock_);
+  for (Chunk* chunk = &chunks_.front(); chunk < &chunks_.back(); ) {
+    if (!chunk->IsFree()) {
+      size_t size = chunk->GetSize();
+      void* begin = AddrFromChunk(chunk);
+      void* end = reinterpret_cast<void*>(reinterpret_cast<byte*>(begin) + size);
+      callback(begin, end, size, arg);
+      callback(NULL, NULL, 0, arg);
+    }
+    chunk = GetNextChunk(chunk);
+  }
+}
+
+size_t FreeListSpace::Free(Thread* self, Object* obj) {
+  MutexLock mu(self, lock_);
+  CHECK(Contains(obj));
+  // Check adjacent chunks to see if we need to combine.
+  Chunk* chunk = ChunkFromAddr(obj);
+  CHECK(!chunk->IsFree());
+
+  size_t allocation_size = chunk->GetSize();
+  madvise(obj, allocation_size, MADV_DONTNEED);
+  num_objects_allocated_--;
+  num_bytes_allocated_ -= allocation_size;
+  Chunk* prev = chunk->GetPrevious();
+  Chunk* next = GetNextChunk(chunk);
+
+  // Combine any adjacent free chunks
+  size_t extra_size = chunk->GetSize();
+  if (next->IsFree()) {
+    extra_size += next->GetSize();
+    RemoveFreeChunk(next);
+  }
+  if (prev != NULL && prev->IsFree()) {
+    RemoveFreeChunk(prev);
+    AddFreeChunk(AddrFromChunk(prev), prev->GetSize() + extra_size, prev->GetPrevious());
+  } else {
+    AddFreeChunk(AddrFromChunk(chunk), extra_size, prev);
+  }
+  return allocation_size;
+}
+
+bool FreeListSpace::Contains(const Object* obj) const {
+  return mem_map_->HasAddress(obj);
+}
+
+FreeListSpace::Chunk* FreeListSpace::GetNextChunk(Chunk* chunk) {
+  return chunk + chunk->GetSize() / kAlignment;
+}
+
+size_t FreeListSpace::AllocationSize(const Object* obj) {
+  Chunk* chunk = ChunkFromAddr(const_cast<Object*>(obj));
+  CHECK(!chunk->IsFree());
+  return chunk->GetSize();
+}
+
+Object* FreeListSpace::Alloc(Thread* self, size_t num_bytes) {
+  MutexLock mu(self, lock_);
+  num_bytes = RoundUp(num_bytes, kAlignment);
+  Chunk temp;
+  temp.SetSize(num_bytes);
+  // Find the smallest chunk at least num_bytes in size.
+  FreeChunks::iterator found = free_chunks_.lower_bound(&temp);
+  if (found == free_chunks_.end()) {
+    // Out of memory, or too much fragmentation.
+    return NULL;
+  }
+  Chunk* chunk = *found;
+  free_chunks_.erase(found);
+  CHECK(chunk->IsFree());
+  void* addr = AddrFromChunk(chunk);
+  size_t chunk_size = chunk->GetSize();
+  chunk->SetSize(num_bytes);
+  if (chunk_size > num_bytes) {
+    // Split the chunk into two chunks.
+    Chunk* new_chunk = GetNextChunk(chunk);
+    AddFreeChunk(AddrFromChunk(new_chunk), chunk_size - num_bytes, chunk);
+  }
+
+  num_objects_allocated_++;
+  total_objects_allocated_++;
+  num_bytes_allocated_ += num_bytes;
+  total_bytes_allocated_ += num_bytes;
+  return reinterpret_cast<Object*>(addr);
+}
+
+}
diff --git a/src/gc/large_object_space.h b/src/gc/large_object_space.h
new file mode 100644
index 0000000..2bf6abf
--- /dev/null
+++ b/src/gc/large_object_space.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_GC_LARGE_OBJECT_SPACE_H_
+#define ART_SRC_GC_LARGE_OBJECT_SPACE_H_
+
+#include "space.h"
+
+namespace art {
+
+class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace {
+ public:
+  virtual bool CanAllocateInto() const {
+    return true;
+  }
+
+  virtual bool IsCompactible() const {
+    return true;
+  }
+
+  virtual SpaceType GetType() const {
+    return kSpaceTypeLargeObjectSpace;
+  }
+
+  virtual SpaceSetMap* GetLiveObjects() const {
+    return live_objects_.get();
+  }
+
+  virtual SpaceSetMap* GetMarkObjects() const {
+    return mark_objects_.get();
+  }
+
+  virtual void SwapBitmaps();
+  virtual void CopyLiveToMarked();
+  virtual void Walk(DlMallocSpace::WalkCallback, void* arg) = 0;
+  virtual ~LargeObjectSpace() {}
+
+  uint64_t GetNumBytesAllocated() const {
+    return num_bytes_allocated_;
+  }
+
+  uint64_t GetNumObjectsAllocated() const {
+    return num_objects_allocated_;
+  }
+
+  uint64_t GetTotalBytesAllocated() const {
+    return total_bytes_allocated_;
+  }
+
+  uint64_t GetTotalObjectsAllocated() const {
+    return total_objects_allocated_;
+  }
+
+  size_t FreeList(Thread* self, size_t num_ptrs, Object** ptrs);
+
+ protected:
+
+  LargeObjectSpace(const std::string& name);
+
+  // Approximate number of bytes which have been allocated into the space.
+  size_t num_bytes_allocated_;
+  size_t num_objects_allocated_;
+  size_t total_bytes_allocated_;
+  size_t total_objects_allocated_;
+
+  UniquePtr<SpaceSetMap> live_objects_;
+  UniquePtr<SpaceSetMap> mark_objects_;
+
+  friend class Space;
+};
+
+class LargeObjectMapSpace : public LargeObjectSpace {
+ public:
+
+  // Creates a large object space. Allocations into the large object space use memory maps instead
+  // of malloc.
+  static LargeObjectMapSpace* Create(const std::string& name);
+
+  // Return the storage space required by obj.
+  virtual size_t AllocationSize(const Object* obj);
+  virtual Object* Alloc(Thread* self, size_t num_bytes);
+  size_t Free(Thread* self, Object* ptr);
+  virtual void Walk(DlMallocSpace::WalkCallback, void* arg);
+  virtual bool Contains(const Object* obj) const;
+private:
+  LargeObjectMapSpace(const std::string& name);
+  virtual ~LargeObjectMapSpace() {}
+
+  // Used to ensure mutual exclusion when the allocation spaces data structures are being modified.
+  mutable Mutex lock_;
+  std::vector<Object*> large_objects_;
+  typedef SafeMap<Object*, MemMap*> MemMaps;
+  MemMaps mem_maps_;
+};
+
+class FreeListSpace : public LargeObjectSpace {
+ public:
+  virtual ~FreeListSpace();
+  static FreeListSpace* Create(const std::string& name, byte* requested_begin, size_t capacity);
+
+  size_t AllocationSize(const Object* obj);
+  Object* Alloc(Thread* self, size_t num_bytes);
+  size_t Free(Thread* self, Object* obj);
+  bool Contains(const Object* obj) const;
+  void Walk(DlMallocSpace::WalkCallback callback, void* arg);
+
+  // Address at which the space begins
+  byte* Begin() const {
+    return begin_;
+  }
+
+  // Address at which the space ends, which may vary as the space is filled.
+  byte* End() const {
+    return end_;
+  }
+
+  // Current size of space
+  size_t Size() const {
+    return End() - Begin();
+  }
+ private:
+  static const size_t kAlignment = kPageSize;
+
+  class Chunk {
+   public:
+    static const size_t kFreeFlag = 0x80000000;
+
+    struct SortBySize {
+      bool operator()(const Chunk* a, const Chunk* b) const {
+        return a->GetSize() < b->GetSize();
+      }
+    };
+
+    bool IsFree() const {
+      return (m_size & kFreeFlag) != 0;
+    }
+
+    void SetSize(size_t size, bool is_free = false) {
+      m_size = size | (is_free ? kFreeFlag : 0);
+    }
+
+    size_t GetSize() const {
+      return m_size & (kFreeFlag - 1);
+    }
+
+    Chunk* GetPrevious() {
+      return m_previous;
+    }
+
+    void SetPrevious(Chunk* previous) {
+      m_previous = previous;
+      DCHECK(m_previous == NULL ||
+            (m_previous != NULL && m_previous + m_previous->GetSize() / kAlignment == this));
+    }
+   private:
+    size_t m_size;
+    Chunk* m_previous;
+  };
+
+  FreeListSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end);
+  void AddFreeChunk(void* address, size_t size, Chunk* previous);
+  Chunk* ChunkFromAddr(void* address);
+  void* AddrFromChunk(Chunk* chunk);
+  void RemoveFreeChunk(Chunk* chunk);
+  Chunk* GetNextChunk(Chunk* chunk);
+
+  typedef std::multiset<Chunk*, Chunk::SortBySize> FreeChunks;
+  byte* begin_;
+  byte* end_;
+  UniquePtr<MemMap> mem_map_;
+  Mutex lock_;
+  std::vector<Chunk> chunks_;
+  FreeChunks free_chunks_;
+};
+
+}
+
+#endif  // ART_SRC_GC_LARGE_OBJECT_SPACE_H_
diff --git a/src/gc/mark_sweep.cc b/src/gc/mark_sweep.cc
index b82bc6e..e19e2c4 100644
--- a/src/gc/mark_sweep.cc
+++ b/src/gc/mark_sweep.cc
@@ -26,6 +26,7 @@
 #include "indirect_reference_table.h"
 #include "intern_table.h"
 #include "jni_internal.h"
+#include "large_object_space.h"
 #include "logging.h"
 #include "macros.h"
 #include "monitor.h"
@@ -208,7 +209,7 @@
 
 void MarkSweep::BindLiveToMarkBitmap(ContinuousSpace* space) {
   CHECK(space->IsAllocSpace());
-  AllocSpace* alloc_space = space->AsAllocSpace();
+  DlMallocSpace* alloc_space = space->AsAllocSpace();
   SpaceBitmap* live_bitmap = space->GetLiveBitmap();
   SpaceBitmap* mark_bitmap = alloc_space->mark_bitmap_.release();
   GetHeap()->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap);
@@ -499,17 +500,12 @@
   // TODO: investigate performance
   static const bool kUseFreeList = true;
   if (kUseFreeList) {
-    for (size_t i = 0; i < num_ptrs; ++i) {
-      Object* obj = static_cast<Object*>(ptrs[i]);
-      freed_bytes += space->AllocationSize(obj);
-    }
     // AllocSpace::FreeList clears the value in ptrs, so perform after clearing the live bit
-    space->FreeList(self, num_ptrs, ptrs);
+    freed_bytes += space->FreeList(self, num_ptrs, ptrs);
   } else {
     for (size_t i = 0; i < num_ptrs; ++i) {
       Object* obj = static_cast<Object*>(ptrs[i]);
-      freed_bytes += space->AllocationSize(obj);
-      space->Free(self, obj);
+      freed_bytes += space->Free(self, obj);
     }
   }
 
@@ -532,7 +528,7 @@
 
 void MarkSweep::SweepArray(TimingLogger& logger, ObjectStack* allocations, bool swap_bitmaps) {
   size_t freed_bytes = 0;
-  AllocSpace* space = heap_->GetAllocSpace();
+  DlMallocSpace* space = heap_->GetAllocSpace();
 
   // If we don't swap bitmaps then newly allocated Weaks go into the live bitmap but not mark
   // bitmap, resulting in occasional frees of Weaks which are still in use.
@@ -566,22 +562,18 @@
       if (!mark_bitmap->Test(obj)) {
         // Don't bother un-marking since we clear the mark bitmap anyways.
         *(out++) = obj;
-        size_t size = space->AllocationSize(obj);
-        freed_bytes += size;
       }
     } else if (!large_mark_objects->Test(obj)) {
       ++freed_large_objects;
-      size_t size = large_object_space->AllocationSize(obj);
-      freed_bytes += size;
-      large_object_space->Free(self, obj);
+      freed_bytes += large_object_space->Free(self, obj);
     }
   }
   logger.AddSplit("Process allocation stack");
 
   size_t freed_objects = out - objects;
+  freed_bytes += space->FreeList(self, freed_objects, objects);
   VLOG(heap) << "Freed " << freed_objects << "/" << count
              << " objects with size " << PrettySize(freed_bytes);
-  space->FreeList(self, freed_objects, objects);
   heap_->RecordFree(freed_objects + freed_large_objects, freed_bytes);
   freed_objects_ += freed_objects;
   freed_bytes_ += freed_bytes;
@@ -645,8 +637,7 @@
   Thread* self = Thread::Current();
   for (SpaceSetMap::Objects::iterator it = live_objects.begin(); it != live_objects.end(); ++it) {
     if (!large_mark_objects->Test(*it)) {
-      freed_bytes += large_object_space->AllocationSize(*it);
-      large_object_space->Free(self, const_cast<Object*>(*it));
+      freed_bytes += large_object_space->Free(self, const_cast<Object*>(*it));
       ++freed_objects;
     }
   }
@@ -1017,7 +1008,7 @@
   for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) {
     Space* space = *it;
     if (space->IsAllocSpace()) {
-      AllocSpace* alloc_space = space->AsAllocSpace();
+      DlMallocSpace* alloc_space = space->AsAllocSpace();
       if (alloc_space->temp_bitmap_.get() != NULL) {
         // At this point, the temp_bitmap holds our old mark bitmap.
         SpaceBitmap* new_bitmap = alloc_space->temp_bitmap_.release();
diff --git a/src/gc/space.cc b/src/gc/space.cc
index 274d777..4d5ce93 100644
--- a/src/gc/space.cc
+++ b/src/gc/space.cc
@@ -28,11 +28,6 @@
 
 namespace art {
 
-#ifndef NDEBUG
-static const bool kDebugSpaces = true;
-#else
-static const bool kDebugSpaces = false;
-#endif
 // Magic padding value that we use to check for buffer overruns.
 static const word kPaddingValue = 0xBAC0BAC0;
 
@@ -46,17 +41,35 @@
     } \
   } while (false)
 
-Space::Space(const std::string& name, GcRetentionPolicy gc_retention_policy)
-    : name_(name),
-      gc_retention_policy_(gc_retention_policy) {
+ImageSpace* Space::AsImageSpace() {
+  DCHECK_EQ(GetType(), kSpaceTypeImageSpace);
+  return down_cast<ImageSpace*>(down_cast<MemMapSpace*>(this));
+}
 
+DlMallocSpace* Space::AsAllocSpace() {
+  DCHECK_EQ(GetType(), kSpaceTypeAllocSpace);
+  return down_cast<DlMallocSpace*>(down_cast<MemMapSpace*>(this));
+}
+
+DlMallocSpace* Space::AsZygoteSpace() {
+  DCHECK_EQ(GetType(), kSpaceTypeZygoteSpace);
+  return down_cast<DlMallocSpace*>(down_cast<MemMapSpace*>(this));
+}
+
+LargeObjectSpace* Space::AsLargeObjectSpace() {
+  DCHECK_EQ(GetType(), kSpaceTypeLargeObjectSpace);
+  return reinterpret_cast<LargeObjectSpace*>(this);
 }
 
 ContinuousSpace::ContinuousSpace(const std::string& name, byte* begin, byte* end,
                                  GcRetentionPolicy gc_retention_policy)
-    : Space(name, gc_retention_policy),
-      begin_(begin),
-      end_(end) {
+    : name_(name), gc_retention_policy_(gc_retention_policy), begin_(begin), end_(end) {
+
+}
+
+DiscontinuousSpace::DiscontinuousSpace(const std::string& name,
+                                       GcRetentionPolicy gc_retention_policy)
+    : name_(name), gc_retention_policy_(gc_retention_policy) {
 
 }
 
@@ -68,9 +81,9 @@
 
 }
 
-size_t AllocSpace::bitmap_index_ = 0;
+size_t DlMallocSpace::bitmap_index_ = 0;
 
-AllocSpace::AllocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin,
+DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin,
                        byte* end, size_t growth_limit)
     : MemMapSpace(name, mem_map, end - begin, kGcRetentionPolicyAlwaysCollect),
       num_bytes_allocated_(0), num_objects_allocated_(0), total_bytes_allocated_(0),
@@ -94,8 +107,8 @@
   DCHECK(live_bitmap_.get() != NULL) << "could not create allocspace mark bitmap #" << bitmap_index;
 }
 
-AllocSpace* AllocSpace::Create(const std::string& name, size_t initial_size, size_t growth_limit,
-                               size_t capacity, byte* requested_begin) {
+DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_size, size_t
+                                     growth_limit, size_t capacity, byte* requested_begin) {
   // Memory we promise to dlmalloc before it asks for morecore.
   // Note: making this value large means that large allocations are unlikely to succeed as dlmalloc
   // will ask for this memory from sys_alloc which will fail as the footprint (this value plus the
@@ -140,7 +153,7 @@
     return NULL;
   }
 
-  void* mspace = AllocSpace::CreateMallocSpace(mem_map->Begin(), starting_size, initial_size);
+  void* mspace = CreateMallocSpace(mem_map->Begin(), starting_size, initial_size);
   if (mspace == NULL) {
     LOG(ERROR) << "Failed to initialize mspace for alloc space (" << name << ")";
     return NULL;
@@ -154,8 +167,8 @@
 
   // Everything is set so record in immutable structure and leave
   MemMap* mem_map_ptr = mem_map.release();
-  AllocSpace* space = new AllocSpace(name, mem_map_ptr, mspace, mem_map_ptr->Begin(), end,
-                                     growth_limit);
+  DlMallocSpace* space = new DlMallocSpace(name, mem_map_ptr, mspace, mem_map_ptr->Begin(), end,
+                                           growth_limit);
   if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
     LOG(INFO) << "Space::CreateAllocSpace exiting (" << PrettyDuration(NanoTime() - start_time)
         << " ) " << *space;
@@ -163,7 +176,7 @@
   return space;
 }
 
-void* AllocSpace::CreateMallocSpace(void* begin, size_t morecore_start, size_t initial_size) {
+void* DlMallocSpace::CreateMallocSpace(void* begin, size_t morecore_start, size_t initial_size) {
   // clear errno to allow PLOG on error
   errno = 0;
   // create mspace using our backing storage starting at begin and with a footprint of
@@ -179,7 +192,7 @@
   return msp;
 }
 
-void AllocSpace::SwapBitmaps() {
+void DlMallocSpace::SwapBitmaps() {
   SpaceBitmap* temp_live_bitmap = live_bitmap_.release();
   live_bitmap_.reset(mark_bitmap_.release());
   mark_bitmap_.reset(temp_live_bitmap);
@@ -189,7 +202,7 @@
   mark_bitmap_->SetName(temp_name);
 }
 
-Object* AllocSpace::AllocWithoutGrowthLocked(size_t num_bytes) {
+Object* DlMallocSpace::AllocWithoutGrowthLocked(size_t num_bytes) {
   if (kDebugSpaces) {
     num_bytes += sizeof(word);
   }
@@ -210,28 +223,27 @@
   return result;
 }
 
-Object* AllocSpace::AllocWithoutGrowth(Thread* self, size_t num_bytes) {
+Object* DlMallocSpace::Alloc(Thread* self, size_t num_bytes) {
   MutexLock mu(self, lock_);
   return AllocWithoutGrowthLocked(num_bytes);
 }
 
-Object* AllocSpace::AllocWithGrowth(Thread* self, size_t num_bytes) {
+Object* DlMallocSpace::AllocWithGrowth(Thread* self, size_t num_bytes) {
   MutexLock mu(self, lock_);
   // Grow as much as possible within the mspace.
   size_t max_allowed = Capacity();
   mspace_set_footprint_limit(mspace_, max_allowed);
   // Try the allocation.
-  void* ptr = AllocWithoutGrowthLocked(num_bytes);
+  Object* result = AllocWithoutGrowthLocked(num_bytes);
   // Shrink back down as small as possible.
   size_t footprint = mspace_footprint(mspace_);
   mspace_set_footprint_limit(mspace_, footprint);
   // Return the new allocation or NULL.
-  Object* result = reinterpret_cast<Object*>(ptr);
   CHECK(!kDebugSpaces || result == NULL || Contains(result));
   return result;
 }
 
-void AllocSpace::SetGrowthLimit(size_t growth_limit) {
+void DlMallocSpace::SetGrowthLimit(size_t growth_limit) {
   growth_limit = RoundUp(growth_limit, kPageSize);
   growth_limit_ = growth_limit;
   if (Size() > growth_limit_) {
@@ -239,7 +251,7 @@
   }
 }
 
-AllocSpace* AllocSpace::CreateZygoteSpace() {
+DlMallocSpace* DlMallocSpace::CreateZygoteSpace() {
   end_ = reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(end_), kPageSize));
   DCHECK(IsAligned<CardTable::kCardSize>(begin_));
   DCHECK(IsAligned<CardTable::kCardSize>(end_));
@@ -276,7 +288,8 @@
   if (capacity - initial_size > 0) {
     CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), name_.c_str());
   }
-  AllocSpace* alloc_space = new AllocSpace(name_, mem_map.release(), mspace, end_, end, growth_limit);
+  DlMallocSpace* alloc_space =
+      new DlMallocSpace(name_, mem_map.release(), mspace, end_, end, growth_limit);
   live_bitmap_->SetHeapLimit(reinterpret_cast<uintptr_t>(End()));
   CHECK_EQ(live_bitmap_->HeapLimit(), reinterpret_cast<uintptr_t>(End()));
   mark_bitmap_->SetHeapLimit(reinterpret_cast<uintptr_t>(End()));
@@ -286,7 +299,7 @@
   return alloc_space;
 }
 
-void AllocSpace::Free(Thread* self, Object* ptr) {
+size_t DlMallocSpace::Free(Thread* self, Object* ptr) {
   MutexLock mu(self, lock_);
   if (kDebugSpaces) {
     CHECK(ptr != NULL);
@@ -295,12 +308,20 @@
         *reinterpret_cast<word*>(reinterpret_cast<byte*>(ptr) + AllocationSize(ptr) -
             sizeof(word) - kChunkOverhead), kPaddingValue);
   }
-  num_bytes_allocated_ -= AllocationSize(ptr);
+  const size_t bytes_freed = AllocationSize(ptr);
+  num_bytes_allocated_ -= bytes_freed;
   --num_objects_allocated_;
   mspace_free(mspace_, ptr);
+  return bytes_freed;
 }
 
-void AllocSpace::FreeList(Thread* self, size_t num_ptrs, Object** ptrs) {
+size_t DlMallocSpace::FreeList(Thread* self, size_t num_ptrs, Object** ptrs) {
+  // Don't need the lock to calculate the size of the freed pointers.
+  size_t bytes_freed = 0;
+  for (size_t i = 0; i < num_ptrs; i++) {
+    bytes_freed += AllocationSize(ptrs[i]);
+  }
+
   MutexLock mu(self, lock_);
   if (kDebugSpaces) {
     CHECK(ptrs != NULL);
@@ -316,11 +337,10 @@
     }
     CHECK_EQ(num_broken_ptrs, 0u);
   }
-  for (size_t i = 0; i < num_ptrs; i++) {
-    num_bytes_allocated_ -= AllocationSize(ptrs[i]);
-  }
+  num_bytes_allocated_ -= bytes_freed;
   num_objects_allocated_ -= num_ptrs;
   mspace_bulk_free(mspace_, reinterpret_cast<void**>(ptrs), num_ptrs);
+  return bytes_freed;
 }
 
 // Callback from dlmalloc when it needs to increase the footprint
@@ -330,7 +350,7 @@
   return heap->GetAllocSpace()->MoreCore(increment);
 }
 
-void* AllocSpace::MoreCore(intptr_t increment) {
+void* DlMallocSpace::MoreCore(intptr_t increment) {
   lock_.AssertHeld(Thread::Current());
   byte* original_end = end_;
   if (increment != 0) {
@@ -364,7 +384,7 @@
   return original_end;
 }
 
-size_t AllocSpace::AllocationSize(const Object* obj) {
+size_t DlMallocSpace::AllocationSize(const Object* obj) {
   return mspace_usable_size(const_cast<void*>(reinterpret_cast<const void*>(obj))) +
       kChunkOverhead;
 }
@@ -383,7 +403,7 @@
   }
 }
 
-void AllocSpace::Trim() {
+void DlMallocSpace::Trim() {
   MutexLock mu(Thread::Current(), lock_);
   // Trim to release memory at the end of the space.
   mspace_trim(mspace_, 0);
@@ -391,21 +411,21 @@
   mspace_inspect_all(mspace_, MspaceMadviseCallback, NULL);
 }
 
-void AllocSpace::Walk(void(*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
+void DlMallocSpace::Walk(void(*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
                       void* arg) {
   MutexLock mu(Thread::Current(), lock_);
   mspace_inspect_all(mspace_, callback, arg);
   callback(NULL, NULL, 0, arg);  // Indicate end of a space.
 }
 
-size_t AllocSpace::GetFootprintLimit() {
+size_t DlMallocSpace::GetFootprintLimit() {
   MutexLock mu(Thread::Current(), lock_);
   return mspace_footprint_limit(mspace_);
 }
 
-void AllocSpace::SetFootprintLimit(size_t new_size) {
+void DlMallocSpace::SetFootprintLimit(size_t new_size) {
   MutexLock mu(Thread::Current(), lock_);
-  VLOG(heap) << "AllocSpace::SetFootprintLimit " << PrettySize(new_size);
+  VLOG(heap) << "DLMallocSpace::SetFootprintLimit " << PrettySize(new_size);
   // Compare against the actual footprint, rather than the Size(), because the heap may not have
   // grown all the way to the allowed size yet.
   size_t current_space_size = mspace_footprint(mspace_);
@@ -414,6 +434,7 @@
     new_size = current_space_size;
   }
   mspace_set_footprint_limit(mspace_, new_size);
+  LOG(INFO) << "Setting footprint limit to " << new_size;
 }
 
 size_t ImageSpace::bitmap_index_ = 0;
@@ -521,7 +542,7 @@
   return os;
 }
 
-void AllocSpace::Dump(std::ostream& os) const {
+void DlMallocSpace::Dump(std::ostream& os) const {
   os << GetType()
       << "begin=" << reinterpret_cast<void*>(Begin())
       << ",end=" << reinterpret_cast<void*>(End())
@@ -537,242 +558,4 @@
       << ",name=\"" << GetName() << "\"]";
 }
 
-void LargeObjectSpace::SwapBitmaps() {
-  SpaceSetMap* temp_live_objects = live_objects_.release();
-  live_objects_.reset(mark_objects_.release());
-  mark_objects_.reset(temp_live_objects);
-  // Swap names to get more descriptive diagnostics.
-  std::string temp_name = live_objects_->GetName();
-  live_objects_->SetName(mark_objects_->GetName());
-  mark_objects_->SetName(temp_name);
-}
-
-DiscontinuousSpace::DiscontinuousSpace(const std::string& name,
-                                       GcRetentionPolicy gc_retention_policy)
-    : Space(name, gc_retention_policy) {
-
-}
-
-LargeObjectSpace::LargeObjectSpace(const std::string& name)
-    : DiscontinuousSpace(name, kGcRetentionPolicyAlwaysCollect),
-      num_bytes_allocated_(0), num_objects_allocated_(0), total_bytes_allocated_(0),
-      total_objects_allocated_(0) {
-  live_objects_.reset(new SpaceSetMap("large live objects"));
-  mark_objects_.reset(new SpaceSetMap("large marked objects"));
-}
-
-
-void LargeObjectSpace::CopyLiveToMarked() {
-  mark_objects_->CopyFrom(*live_objects_.get());
-}
-
-LargeObjectMapSpace::LargeObjectMapSpace(const std::string& name)
-    : LargeObjectSpace(name),
-      lock_("large object space lock", kAllocSpaceLock)
-{
-
-}
-
-LargeObjectMapSpace* LargeObjectMapSpace::Create(const std::string& name) {
-  return new LargeObjectMapSpace(name);
-}
-
-Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes) {
-  MemMap* mem_map = MemMap::MapAnonymous("allocation", NULL, num_bytes, PROT_READ | PROT_WRITE);
-  if (mem_map == NULL) {
-    return NULL;
-  }
-  MutexLock mu(self, lock_);
-  Object* obj = reinterpret_cast<Object*>(mem_map->Begin());
-  large_objects_.push_back(obj);
-  mem_maps_.Put(obj, mem_map);
-  size_t allocation_size = mem_map->Size();
-  num_bytes_allocated_ += allocation_size;
-  total_bytes_allocated_ += allocation_size;
-  ++num_objects_allocated_;
-  ++total_objects_allocated_;
-  return obj;
-}
-
-void LargeObjectMapSpace::Free(Thread* self, Object* ptr) {
-  MutexLock mu(self, lock_);
-  MemMaps::iterator found = mem_maps_.find(ptr);
-  CHECK(found != mem_maps_.end()) << "Attempted to free large object which was not live";
-  DCHECK_GE(num_bytes_allocated_, found->second->Size());
-  num_bytes_allocated_ -= found->second->Size();
-  --num_objects_allocated_;
-  delete found->second;
-  mem_maps_.erase(found);
-}
-
-size_t LargeObjectMapSpace::AllocationSize(const Object* obj) {
-  MutexLock mu(Thread::Current(), lock_);
-  MemMaps::iterator found = mem_maps_.find(const_cast<Object*>(obj));
-  CHECK(found != mem_maps_.end()) << "Attempted to get size of a large object which is not live";
-  return found->second->Size();
-}
-
-void LargeObjectMapSpace::Walk(AllocSpace::WalkCallback callback, void* arg) {
-  MutexLock mu(Thread::Current(), lock_);
-  for (MemMaps::iterator it = mem_maps_.begin(); it != mem_maps_.end(); ++it) {
-    MemMap* mem_map = it->second;
-    callback(mem_map->Begin(), mem_map->End(), mem_map->Size(), arg);
-    callback(NULL, NULL, 0, arg);
-  }
-}
-
-bool LargeObjectMapSpace::Contains(const Object* obj) const {
-  MutexLock mu(Thread::Current(), lock_);
-  return mem_maps_.find(const_cast<Object*>(obj)) != mem_maps_.end();
-}
-
-FreeListSpace* FreeListSpace::Create(const std::string& name, byte* requested_begin, size_t size) {
-  CHECK(size % kAlignment == 0);
-  MemMap* mem_map = MemMap::MapAnonymous(name.c_str(), requested_begin, size,
-                                         PROT_READ | PROT_WRITE);
-  CHECK(mem_map != NULL) << "Failed to allocate large object space mem map";
-  return new FreeListSpace(name, mem_map, mem_map->Begin(), mem_map->End());
-}
-
-FreeListSpace::FreeListSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end)
-    : LargeObjectSpace(name),
-      begin_(begin),
-      end_(end),
-      mem_map_(mem_map),
-      lock_("free list space lock", kAllocSpaceLock) {
-  chunks_.resize(Size() / kAlignment + 1);
-  // Add a dummy chunk so we don't need to handle chunks having no next chunk.
-  chunks_.back().SetSize(kAlignment, false);
-  // Start out with one large free chunk.
-  AddFreeChunk(begin_, end_ - begin_, NULL);
-}
-
-FreeListSpace::~FreeListSpace() {
-
-}
-
-void FreeListSpace::AddFreeChunk(void* address, size_t size, Chunk* previous) {
-  Chunk* chunk = ChunkFromAddr(address);
-  chunk->SetSize(size, true);
-  chunk->SetPrevious(previous);
-  Chunk* next_chunk = GetNextChunk(chunk);
-  next_chunk->SetPrevious(chunk);
-  free_chunks_.insert(chunk);
-}
-
-FreeListSpace::Chunk* FreeListSpace::ChunkFromAddr(void* address) {
-  size_t offset = reinterpret_cast<byte*>(address) - Begin();
-  DCHECK(IsAligned<kAlignment>(offset));
-  DCHECK_LT(offset, Size());
-  return &chunks_[offset / kAlignment];
-}
-
-void* FreeListSpace::AddrFromChunk(Chunk* chunk) {
-  return reinterpret_cast<void*>(Begin() + (chunk - &chunks_.front()) * kAlignment);
-}
-
-void FreeListSpace::RemoveFreeChunk(Chunk* chunk) {
-  // TODO: C++0x
-  // TODO: Improve performance, this might be slow.
-  std::pair<FreeChunks::iterator, FreeChunks::iterator> range = free_chunks_.equal_range(chunk);
-  for (FreeChunks::iterator it = range.first; it != range.second; ++it) {
-    if (*it == chunk) {
-      free_chunks_.erase(it);
-      return;
-    }
-  }
-}
-
-void FreeListSpace::Walk(AllocSpace::WalkCallback callback, void* arg) {
-  MutexLock mu(Thread::Current(), lock_);
-  for (Chunk* chunk = &chunks_.front(); chunk < &chunks_.back(); ) {
-    if (!chunk->IsFree()) {
-      size_t size = chunk->GetSize();
-      void* begin = AddrFromChunk(chunk);
-      void* end = reinterpret_cast<void*>(reinterpret_cast<byte*>(begin) + size);
-      callback(begin, end, size, arg);
-      callback(NULL, NULL, 0, arg);
-    }
-    chunk = GetNextChunk(chunk);
-  }
-}
-
-void FreeListSpace::Free(Thread* self, Object* obj) {
-  MutexLock mu(self, lock_);
-  CHECK(Contains(obj));
-  // Check adjacent chunks to see if we need to combine.
-  Chunk* chunk = ChunkFromAddr(obj);
-  CHECK(!chunk->IsFree());
-
-  size_t allocation_size = chunk->GetSize();
-  madvise(obj, allocation_size, MADV_DONTNEED);
-  num_objects_allocated_--;
-  num_bytes_allocated_ -= allocation_size;
-  Chunk* prev = chunk->GetPrevious();
-  Chunk* next = GetNextChunk(chunk);
-
-  // Combine any adjacent free chunks
-  size_t extra_size = chunk->GetSize();
-  if (next->IsFree()) {
-    extra_size += next->GetSize();
-    RemoveFreeChunk(next);
-  }
-  if (prev != NULL && prev->IsFree()) {
-    RemoveFreeChunk(prev);
-    AddFreeChunk(AddrFromChunk(prev), prev->GetSize() + extra_size, prev->GetPrevious());
-  } else {
-    AddFreeChunk(AddrFromChunk(chunk), extra_size, prev);
-  }
-}
-
-bool FreeListSpace::Contains(const Object* obj) const {
-  return mem_map_->HasAddress(obj);
-}
-
-FreeListSpace::Chunk* FreeListSpace::GetNextChunk(Chunk* chunk) {
-  return chunk + chunk->GetSize() / kAlignment;
-}
-
-size_t FreeListSpace::AllocationSize(const Object* obj) {
-  Chunk* chunk = ChunkFromAddr(const_cast<Object*>(obj));
-  CHECK(!chunk->IsFree());
-  return chunk->GetSize();
-}
-
-Object* FreeListSpace::Alloc(Thread* self, size_t num_bytes) {
-  MutexLock mu(self, lock_);
-  num_bytes = RoundUp(num_bytes, kAlignment);
-  Chunk temp;
-  temp.SetSize(num_bytes);
-  // Find the smallest chunk at least num_bytes in size.
-  FreeChunks::iterator found = free_chunks_.lower_bound(&temp);
-  if (found == free_chunks_.end()) {
-    // Out of memory, or too much fragmentation.
-    return NULL;
-  }
-  Chunk* chunk = *found;
-  free_chunks_.erase(found);
-  CHECK(chunk->IsFree());
-  void* addr = AddrFromChunk(chunk);
-  size_t chunk_size = chunk->GetSize();
-  chunk->SetSize(num_bytes);
-  if (chunk_size > num_bytes) {
-    // Split the chunk into two chunks.
-    Chunk* new_chunk = GetNextChunk(chunk);
-    AddFreeChunk(AddrFromChunk(new_chunk), chunk_size - num_bytes, chunk);
-  }
-
-  num_objects_allocated_++;
-  total_objects_allocated_++;
-  num_bytes_allocated_ += num_bytes;
-  total_bytes_allocated_ += num_bytes;
-  return reinterpret_cast<Object*>(addr);
-}
-
-void FreeListSpace::FreeList(Thread* self, size_t num_ptrs, Object** ptrs) {
-  for (size_t i = 0; i < num_ptrs; ++i) {
-    Free(self, ptrs[i]);
-  }
-}
-
 }  // namespace art
diff --git a/src/gc/space.h b/src/gc/space.h
index ab29582..81b03fa 100644
--- a/src/gc/space.h
+++ b/src/gc/space.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_SRC_SPACE_H_
-#define ART_SRC_SPACE_H_
+#ifndef ART_SRC_GC_SPACE_H_
+#define ART_SRC_GC_SPACE_H_
 
 #include <string>
 
@@ -29,7 +29,13 @@
 
 namespace art {
 
-class AllocSpace;
+#ifndef NDEBUG
+static const bool kDebugSpaces = true;
+#else
+static const bool kDebugSpaces = false;
+#endif
+
+class DlMallocSpace;
 class ImageSpace;
 class LargeObjectSpace;
 class Object;
@@ -57,38 +63,13 @@
   virtual bool IsCompactible() const = 0;
   virtual bool Contains(const Object* obj) const = 0;
   virtual SpaceType GetType() const = 0;
+  virtual GcRetentionPolicy GetGcRetentionPolicy() const = 0;
+  virtual std::string GetName() const = 0;
 
-  const std::string& GetName() const {
-    return name_;
-  }
-
-  GcRetentionPolicy GetGcRetentionPolicy() const {
-    return gc_retention_policy_;
-  }
-
-  void SetGcRetentionPolicy(GcRetentionPolicy gc_retention_policy) {
-    gc_retention_policy_ = gc_retention_policy;
-  }
-
-  ImageSpace* AsImageSpace() {
-    DCHECK_EQ(GetType(), kSpaceTypeImageSpace);
-    return down_cast<ImageSpace*>(this);
-  }
-
-  AllocSpace* AsAllocSpace() {
-    DCHECK_EQ(GetType(), kSpaceTypeAllocSpace);
-    return down_cast<AllocSpace*>(this);
-  }
-
-  AllocSpace* AsZygoteSpace() {
-    DCHECK_EQ(GetType(), kSpaceTypeZygoteSpace);
-    return down_cast<AllocSpace*>(this);
-  }
-
-  LargeObjectSpace* AsLargeObjectSpace() {
-    DCHECK_EQ(GetType(), kSpaceTypeLargeObjectSpace);
-    return down_cast<LargeObjectSpace*>(this);
-  }
+  ImageSpace* AsImageSpace();
+  DlMallocSpace* AsAllocSpace();
+  DlMallocSpace* AsZygoteSpace();
+  LargeObjectSpace* AsLargeObjectSpace();
 
   bool IsImageSpace() const {
     return GetType() == kSpaceTypeImageSpace;
@@ -111,18 +92,45 @@
   virtual ~Space() {}
 
  protected:
-  Space(const std::string& name, GcRetentionPolicy gc_retention_policy);
-
-  // Name of the space.
-  std::string name_;
-
-  // Garbage collection retention policy, used to figure out when we should sweep over this space.
-  GcRetentionPolicy gc_retention_policy_;
+  Space() { }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(Space);
 };
 
+// AllocSpace interface.
+class AllocSpace {
+ public:
+  virtual bool CanAllocateInto() const {
+    return true;
+  }
+
+  // General statistics
+  virtual uint64_t GetNumBytesAllocated() const = 0;
+  virtual uint64_t GetNumObjectsAllocated() const = 0;
+  virtual uint64_t GetTotalBytesAllocated() const = 0;
+  virtual uint64_t GetTotalObjectsAllocated() const = 0;
+
+  // Allocate num_bytes without allowing growth.
+  virtual Object* Alloc(Thread* self, size_t num_bytes) = 0;
+
+  // Return the storage space required by obj.
+  virtual size_t AllocationSize(const Object* obj) = 0;
+
+  // Returns how many bytes were freed.
+  virtual size_t Free(Thread* self, Object* ptr) = 0;
+
+  // Returns how many bytes were freed.
+  virtual size_t FreeList(Thread* self, size_t num_ptrs, Object** ptrs) = 0;
+
+ protected:
+  AllocSpace() {}
+  virtual ~AllocSpace() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AllocSpace);
+};
+
 // Continuous spaces have bitmaps, and an address range.
 class ContinuousSpace : public Space {
  public:
@@ -156,10 +164,21 @@
 
   virtual ~ContinuousSpace() {}
 
+  virtual std::string GetName() const {
+    return name_;
+  }
+
+  virtual GcRetentionPolicy GetGcRetentionPolicy() const {
+    return gc_retention_policy_;
+  }
+
  protected:
   ContinuousSpace(const std::string& name, byte* begin, byte* end,
                   GcRetentionPolicy gc_retention_policy);
 
+  std::string name_;
+  GcRetentionPolicy gc_retention_policy_;
+
   // The beginning of the storage for fast access.
   byte* begin_;
 
@@ -170,15 +189,26 @@
   DISALLOW_COPY_AND_ASSIGN(ContinuousSpace);
 };
 
-class DiscontinuousSpace : public Space {
+class DiscontinuousSpace : public virtual Space {
  public:
   // Is object within this space?
   virtual bool Contains(const Object* obj) const = 0;
 
+  virtual std::string GetName() const {
+    return name_;
+  }
+
+  virtual GcRetentionPolicy GetGcRetentionPolicy() const {
+    return gc_retention_policy_;
+  }
+
 protected:
   DiscontinuousSpace(const std::string& name, GcRetentionPolicy gc_retention_policy);
 
 private:
+  std::string name_;
+  GcRetentionPolicy gc_retention_policy_;
+
   DISALLOW_COPY_AND_ASSIGN(DiscontinuousSpace);
 };
 
@@ -217,7 +247,7 @@
 };
 
 // An alloc space is a space where objects may be allocated and garbage collected.
-class AllocSpace : public MemMapSpace {
+class DlMallocSpace : public MemMapSpace, public AllocSpace {
  public:
   typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg);
 
@@ -237,19 +267,19 @@
   // base address is not guaranteed to be granted, if it is required,
   // the caller should call Begin on the returned space to confirm
   // the request was granted.
-  static AllocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit,
+  static DlMallocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit,
                             size_t capacity, byte* requested_begin);
 
   // Allocate num_bytes without allowing the underlying mspace to grow.
   virtual Object* AllocWithGrowth(Thread* self, size_t num_bytes);
 
   // Allocate num_bytes allowing the underlying mspace to grow.
-  virtual Object* AllocWithoutGrowth(Thread* self, size_t num_bytes);
+  virtual Object* Alloc(Thread* self, size_t num_bytes);
 
   // Return the storage space required by obj.
   virtual size_t AllocationSize(const Object* obj);
-  virtual void Free(Thread* self, Object* ptr);
-  virtual void FreeList(Thread* self, size_t num_ptrs, Object** ptrs);
+  virtual size_t Free(Thread* self, Object* ptr);
+  virtual size_t FreeList(Thread* self, size_t num_ptrs, Object** ptrs);
 
   void* MoreCore(intptr_t increment);
 
@@ -304,21 +334,25 @@
   virtual void SwapBitmaps();
 
   // Turn ourself into a zygote space and return a new alloc space which has our unused memory.
-  AllocSpace* CreateZygoteSpace();
+  DlMallocSpace* CreateZygoteSpace();
 
-  size_t GetNumBytesAllocated() const {
+  void SetGcRetentionPolicy(GcRetentionPolicy gc_retention_policy) {
+    gc_retention_policy_ = gc_retention_policy;
+  }
+
+  virtual uint64_t GetNumBytesAllocated() const {
     return num_bytes_allocated_;
   }
 
-  size_t GetNumObjectsAllocated() const {
+  virtual uint64_t GetNumObjectsAllocated() const {
     return num_objects_allocated_;
   }
 
-  size_t GetTotalBytesAllocated() const {
+  virtual uint64_t GetTotalBytesAllocated() const {
     return total_bytes_allocated_;
   }
 
-  size_t GetTotalObjectsAllocated() const {
+  virtual uint64_t GetTotalObjectsAllocated() const {
     return total_objects_allocated_;
   }
 
@@ -337,7 +371,7 @@
 
   static size_t bitmap_index_;
 
-  AllocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end,
+  DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end,
              size_t growth_limit);
 
   bool Init(size_t initial_size, size_t maximum_size, size_t growth_size, byte* requested_base);
@@ -364,7 +398,7 @@
 
   friend class MarkSweep;
 
-  DISALLOW_COPY_AND_ASSIGN(AllocSpace);
+  DISALLOW_COPY_AND_ASSIGN(DlMallocSpace);
 };
 
 // An image space is a space backed with a memory mapped image
@@ -390,7 +424,7 @@
     return *reinterpret_cast<ImageHeader*>(Begin());
   }
 
-  const std::string& GetImageFilename() const {
+  const std::string GetImageFilename() const {
     return GetName();
   }
 
@@ -421,181 +455,10 @@
   DISALLOW_COPY_AND_ASSIGN(ImageSpace);
 };
 
-class LargeObjectSpace : public DiscontinuousSpace {
- public:
-  virtual bool CanAllocateInto() const {
-    return true;
-  }
-
-  virtual bool IsCompactible() const {
-    return true;
-  }
-
-  virtual SpaceType GetType() const {
-    return kSpaceTypeLargeObjectSpace;
-  }
-
-  virtual SpaceSetMap* GetLiveObjects() const {
-    return live_objects_.get();
-  }
-
-  virtual SpaceSetMap* GetMarkObjects() const {
-    return mark_objects_.get();
-  }
-
-  virtual void SwapBitmaps();
-  virtual void CopyLiveToMarked();
-
-  // Return the storage space required by obj.
-  virtual size_t AllocationSize(const Object* obj) = 0;
-  virtual Object* Alloc(Thread* self, size_t num_bytes) = 0;
-  virtual void Free(Thread* self, Object* ptr) = 0;
-  virtual void Walk(AllocSpace::WalkCallback, void* arg) = 0;
-
-  virtual ~LargeObjectSpace() {}
-
-
-  size_t GetNumBytesAllocated() const {
-    return num_bytes_allocated_;
-  }
-
-  size_t GetNumObjectsAllocated() const {
-    return num_objects_allocated_;
-  }
-
-  size_t GetTotalBytesAllocated() const {
-    return total_bytes_allocated_;
-  }
-
-  size_t GetTotalObjectsAllocated() const {
-    return total_objects_allocated_;
-  }
-
- protected:
-
-  LargeObjectSpace(const std::string& name);
-
-  // Approximate number of bytes which have been allocated into the space.
-  size_t num_bytes_allocated_;
-  size_t num_objects_allocated_;
-  size_t total_bytes_allocated_;
-  size_t total_objects_allocated_;
-
-  UniquePtr<SpaceSetMap> live_objects_;
-  UniquePtr<SpaceSetMap> mark_objects_;
-
-  friend class Space;
-};
-
-class LargeObjectMapSpace : public LargeObjectSpace {
- public:
-
-  // Creates a large object space. Allocations into the large object space use memory maps instead
-  // of malloc.
-  static LargeObjectMapSpace* Create(const std::string& name);
-
-  // Return the storage space required by obj.
-  virtual size_t AllocationSize(const Object* obj);
-  virtual Object* Alloc(Thread* self, size_t num_bytes);
-  virtual void Free(Thread* self, Object* ptr);
-  virtual void Walk(AllocSpace::WalkCallback, void* arg);
-  virtual bool Contains(const Object* obj) const;
-private:
-  LargeObjectMapSpace(const std::string& name);
-  virtual ~LargeObjectMapSpace() {}
-
-  // Used to ensure mutual exclusion when the allocation spaces data structures are being modified.
-  mutable Mutex lock_;
-  std::vector<Object*> large_objects_;
-  typedef SafeMap<Object*, MemMap*> MemMaps;
-  MemMaps mem_maps_;
-};
-
-class FreeListSpace : public LargeObjectSpace {
- public:
-  virtual ~FreeListSpace();
-  static FreeListSpace* Create(const std::string& name, byte* requested_begin, size_t capacity);
-
-  virtual size_t AllocationSize(const Object* obj);
-  virtual Object* Alloc(Thread* self, size_t num_bytes);
-  virtual void Free(Thread* self, Object* obj);
-  virtual void FreeList(Thread* self, size_t num_ptrs, Object** ptrs);
-  virtual bool Contains(const Object* obj) const;
-  virtual void Walk(AllocSpace::WalkCallback callback, void* arg);
-
-  // Address at which the space begins
-  byte* Begin() const {
-    return begin_;
-  }
-
-  // Address at which the space ends, which may vary as the space is filled.
-  byte* End() const {
-    return end_;
-  }
-
-  // Current size of space
-  size_t Size() const {
-    return End() - Begin();
-  }
- private:
-  static const size_t kAlignment = kPageSize;
-
-  class Chunk {
-   public:
-    static const size_t kFreeFlag = 0x80000000;
-
-    struct SortBySize {
-      bool operator()(const Chunk* a, const Chunk* b) const {
-        return a->GetSize() < b->GetSize();
-      }
-    };
-
-    bool IsFree() const {
-      return (m_size & kFreeFlag) != 0;
-    }
-
-    void SetSize(size_t size, bool is_free = false) {
-      m_size = size | (is_free ? kFreeFlag : 0);
-    }
-
-    size_t GetSize() const {
-      return m_size & (kFreeFlag - 1);
-    }
-
-    Chunk* GetPrevious() {
-      return m_previous;
-    }
-
-    void SetPrevious(Chunk* previous) {
-      m_previous = previous;
-      DCHECK(m_previous == NULL ||
-            (m_previous != NULL && m_previous + m_previous->GetSize() / kAlignment == this));
-    }
-   private:
-    size_t m_size;
-    Chunk* m_previous;
-  };
-
-  FreeListSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end);
-  void AddFreeChunk(void* address, size_t size, Chunk* previous);
-  Chunk* ChunkFromAddr(void* address);
-  void* AddrFromChunk(Chunk* chunk);
-  void RemoveFreeChunk(Chunk* chunk);
-  Chunk* GetNextChunk(Chunk* chunk);
-
-  typedef std::multiset<Chunk*, Chunk::SortBySize> FreeChunks;
-  byte* begin_;
-  byte* end_;
-  UniquePtr<MemMap> mem_map_;
-  Mutex lock_;
-  std::vector<Chunk> chunks_;
-  FreeChunks free_chunks_;
-};
-
 // Callback for dlmalloc_inspect_all or mspace_inspect_all that will madvise(2) unused
 // pages back to the kernel.
 void MspaceMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/);
 
 }  // namespace art
 
-#endif  // ART_SRC_SPACE_H_
+#endif  // ART_SRC_GC_SPACE_H_
diff --git a/src/gc/space_test.cc b/src/gc/space_test.cc
index 0319147..2e03eae 100644
--- a/src/gc/space_test.cc
+++ b/src/gc/space_test.cc
@@ -27,7 +27,7 @@
 
 class SpaceTest : public CommonTest {
  public:
-  void SizeFootPrintGrowthLimitAndTrimBody(AllocSpace* space, intptr_t object_size,
+  void SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr_t object_size,
                                            int round, size_t growth_limit);
   void SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size);
 };
@@ -35,37 +35,37 @@
 TEST_F(SpaceTest, Init) {
   {
     // Init < max == growth
-    UniquePtr<Space> space(AllocSpace::Create("test", 16 * MB, 32 * MB, 32 * MB, NULL));
+    UniquePtr<Space> space(DlMallocSpace::Create("test", 16 * MB, 32 * MB, 32 * MB, NULL));
     EXPECT_TRUE(space.get() != NULL);
   }
   {
     // Init == max == growth
-    UniquePtr<Space> space(AllocSpace::Create("test", 16 * MB, 16 * MB, 16 * MB, NULL));
+    UniquePtr<Space> space(DlMallocSpace::Create("test", 16 * MB, 16 * MB, 16 * MB, NULL));
     EXPECT_TRUE(space.get() != NULL);
   }
   {
     // Init > max == growth
-    UniquePtr<Space> space(AllocSpace::Create("test", 32 * MB, 16 * MB, 16 * MB, NULL));
+    UniquePtr<Space> space(DlMallocSpace::Create("test", 32 * MB, 16 * MB, 16 * MB, NULL));
     EXPECT_TRUE(space.get() == NULL);
   }
   {
     // Growth == init < max
-    UniquePtr<Space> space(AllocSpace::Create("test", 16 * MB, 16 * MB, 32 * MB, NULL));
+    UniquePtr<Space> space(DlMallocSpace::Create("test", 16 * MB, 16 * MB, 32 * MB, NULL));
     EXPECT_TRUE(space.get() != NULL);
   }
   {
     // Growth < init < max
-    UniquePtr<Space> space(AllocSpace::Create("test", 16 * MB, 8 * MB, 32 * MB, NULL));
+    UniquePtr<Space> space(DlMallocSpace::Create("test", 16 * MB, 8 * MB, 32 * MB, NULL));
     EXPECT_TRUE(space.get() == NULL);
   }
   {
     // Init < growth < max
-    UniquePtr<Space> space(AllocSpace::Create("test", 8 * MB, 16 * MB, 32 * MB, NULL));
+    UniquePtr<Space> space(DlMallocSpace::Create("test", 8 * MB, 16 * MB, 32 * MB, NULL));
     EXPECT_TRUE(space.get() != NULL);
   }
   {
     // Init < max < growth
-    UniquePtr<Space> space(AllocSpace::Create("test", 8 * MB, 32 * MB, 16 * MB, NULL));
+    UniquePtr<Space> space(DlMallocSpace::Create("test", 8 * MB, 32 * MB, 16 * MB, NULL));
     EXPECT_TRUE(space.get() == NULL);
   }
 }
@@ -75,7 +75,7 @@
 // allocations after the ZygoteSpace is created. The test should also do some GCs to ensure that
 // the GC works with the ZygoteSpace.
 TEST_F(SpaceTest, ZygoteSpace) {
-    AllocSpace* space(AllocSpace::Create("test", 4 * MB, 16 * MB, 16 * MB, NULL));
+    DlMallocSpace* space(DlMallocSpace::Create("test", 4 * MB, 16 * MB, 16 * MB, NULL));
     ASSERT_TRUE(space != NULL);
 
     // Make space findable to the heap, will also delete space when runtime is cleaned up
@@ -83,11 +83,11 @@
     Thread* self = Thread::Current();
 
     // Succeeds, fits without adjusting the footprint limit.
-    Object* ptr1 = space->AllocWithoutGrowth(self, 1 * MB);
+    Object* ptr1 = space->Alloc(self, 1 * MB);
     EXPECT_TRUE(ptr1 != NULL);
 
     // Fails, requires a higher footprint limit.
-    Object* ptr2 = space->AllocWithoutGrowth(self, 8 * MB);
+    Object* ptr2 = space->Alloc(self, 8 * MB);
     EXPECT_TRUE(ptr2 == NULL);
 
     // Succeeds, adjusts the footprint.
@@ -95,7 +95,7 @@
     EXPECT_TRUE(ptr3 != NULL);
 
     // Fails, requires a higher footprint limit.
-    Object* ptr4 = space->AllocWithoutGrowth(self, 8 * MB);
+    Object* ptr4 = space->Alloc(self, 8 * MB);
     EXPECT_TRUE(ptr4 == NULL);
 
     // Also fails, requires a higher allowed footprint.
@@ -104,7 +104,7 @@
 
     // Release some memory.
     size_t free3 = space->AllocationSize(ptr3);
-    space->Free(self, ptr3);
+    EXPECT_EQ(free3, space->Free(self, ptr3));
     EXPECT_LE(8U * MB, free3);
 
     // Succeeds, now that memory has been freed.
@@ -117,18 +117,18 @@
     EXPECT_LE(1U * MB, free1);
 
     // Make sure that the zygote space isn't directly at the start of the space.
-    space->AllocWithoutGrowth(self, 1U * MB);
+    space->Alloc(self, 1U * MB);
     space = space->CreateZygoteSpace();
 
     // Make space findable to the heap, will also delete space when runtime is cleaned up
     Runtime::Current()->GetHeap()->AddSpace(space);
 
     // Succeeds, fits without adjusting the footprint limit.
-    ptr1 = space->AllocWithoutGrowth(self, 1 * MB);
+    ptr1 = space->Alloc(self, 1 * MB);
     EXPECT_TRUE(ptr1 != NULL);
 
     // Fails, requires a higher footprint limit.
-    ptr2 = space->AllocWithoutGrowth(self, 8 * MB);
+    ptr2 = space->Alloc(self, 8 * MB);
     EXPECT_TRUE(ptr2 == NULL);
 
     // Succeeds, adjusts the footprint.
@@ -143,7 +143,7 @@
 }
 
 TEST_F(SpaceTest, AllocAndFree) {
-  AllocSpace* space(AllocSpace::Create("test", 4 * MB, 16 * MB, 16 * MB, NULL));
+  DlMallocSpace* space(DlMallocSpace::Create("test", 4 * MB, 16 * MB, 16 * MB, NULL));
   ASSERT_TRUE(space != NULL);
   Thread* self = Thread::Current();
 
@@ -151,11 +151,11 @@
   Runtime::Current()->GetHeap()->AddSpace(space);
 
   // Succeeds, fits without adjusting the footprint limit.
-  Object* ptr1 = space->AllocWithoutGrowth(self, 1 * MB);
+  Object* ptr1 = space->Alloc(self, 1 * MB);
   EXPECT_TRUE(ptr1 != NULL);
 
   // Fails, requires a higher footprint limit.
-  Object* ptr2 = space->AllocWithoutGrowth(self, 8 * MB);
+  Object* ptr2 = space->Alloc(self, 8 * MB);
   EXPECT_TRUE(ptr2 == NULL);
 
   // Succeeds, adjusts the footprint.
@@ -163,7 +163,7 @@
   EXPECT_TRUE(ptr3 != NULL);
 
   // Fails, requires a higher footprint limit.
-  Object* ptr4 = space->AllocWithoutGrowth(self, 8 * MB);
+  Object* ptr4 = space->Alloc(self, 8 * MB);
   EXPECT_TRUE(ptr4 == NULL);
 
   // Also fails, requires a higher allowed footprint.
@@ -186,7 +186,7 @@
 }
 
 TEST_F(SpaceTest, AllocAndFreeList) {
-  AllocSpace* space(AllocSpace::Create("test", 4 * MB, 16 * MB, 16 * MB, NULL));
+  DlMallocSpace* space(DlMallocSpace::Create("test", 4 * MB, 16 * MB, 16 * MB, NULL));
   ASSERT_TRUE(space != NULL);
 
   // Make space findable to the heap, will also delete space when runtime is cleaned up
@@ -196,7 +196,7 @@
   // Succeeds, fits without adjusting the max allowed footprint.
   Object* lots_of_objects[1024];
   for (size_t i = 0; i < arraysize(lots_of_objects); i++) {
-    lots_of_objects[i] = space->AllocWithoutGrowth(self, 16);
+    lots_of_objects[i] = space->Alloc(self, 16);
     EXPECT_TRUE(lots_of_objects[i] != NULL);
   }
 
@@ -224,7 +224,7 @@
   return rand();
 }
 
-void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(AllocSpace* space, intptr_t object_size,
+void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(DlMallocSpace* space, intptr_t object_size,
                                                     int round, size_t growth_limit) {
   if (((object_size > 0 && object_size >= static_cast<intptr_t>(growth_limit))) ||
       ((object_size < 0 && -object_size >= static_cast<intptr_t>(growth_limit)))) {
@@ -271,7 +271,7 @@
       }
       Object* object;
       if (round <= 1) {
-        object = space->AllocWithoutGrowth(self, alloc_size);
+        object = space->Alloc(self, alloc_size);
       } else {
         object = space->AllocWithGrowth(self, alloc_size);
       }
@@ -350,7 +350,7 @@
   Object* large_object;
   size_t three_quarters_space = (growth_limit / 2) + (growth_limit / 4);
   if (round <= 1) {
-    large_object = space->AllocWithoutGrowth(self, three_quarters_space);
+    large_object = space->Alloc(self, three_quarters_space);
   } else {
     large_object = space->AllocWithGrowth(self, three_quarters_space);
   }
@@ -376,7 +376,7 @@
   size_t initial_size = 4 * MB;
   size_t growth_limit = 8 * MB;
   size_t capacity = 16 * MB;
-  AllocSpace* space(AllocSpace::Create("test", initial_size, growth_limit, capacity, NULL));
+  DlMallocSpace* space(DlMallocSpace::Create("test", initial_size, growth_limit, capacity, NULL));
   ASSERT_TRUE(space != NULL);
 
   // Basic sanity