Merge "Compile time performance improvements focusing on interpret-only."
diff --git a/compiler/dex/quick/arm64/fp_arm64.cc b/compiler/dex/quick/arm64/fp_arm64.cc
index 5d63dd0..a39d151 100644
--- a/compiler/dex/quick/arm64/fp_arm64.cc
+++ b/compiler/dex/quick/arm64/fp_arm64.cc
@@ -426,11 +426,12 @@
RegLocation rl_dest = (is_double) ? InlineTargetWide(info) : InlineTarget(info);
rl_src = (is_double) ? LoadValueWide(rl_src, kFPReg) : LoadValue(rl_src, kFPReg);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ RegStorage r_imm_point5 = (is_double) ? AllocTempDouble() : AllocTempSingle();
RegStorage r_tmp = (is_double) ? AllocTempDouble() : AllocTempSingle();
// 0.5f and 0.5d are encoded in the same way.
- NewLIR2(kA64Fmov2fI | wide, r_tmp.GetReg(), encoded_imm);
- NewLIR3(kA64Fadd3fff | wide, rl_src.reg.GetReg(), rl_src.reg.GetReg(), r_tmp.GetReg());
- NewLIR2((is_double) ? kA64Fcvtms2xS : kA64Fcvtms2ws, rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ NewLIR2(kA64Fmov2fI | wide, r_imm_point5.GetReg(), encoded_imm);
+ NewLIR3(kA64Fadd3fff | wide, r_tmp.GetReg(), rl_src.reg.GetReg(), r_imm_point5.GetReg());
+ NewLIR2((is_double) ? kA64Fcvtms2xS : kA64Fcvtms2ws, rl_result.reg.GetReg(), r_tmp.GetReg());
(is_double) ? StoreValueWide(rl_dest, rl_result) : StoreValue(rl_dest, rl_result);
return true;
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 2048160..bb7da0d 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -83,8 +83,13 @@
// relative to partial/full GC. This may be desirable since sticky GCs interfere less with mutator
// threads (lower pauses, use less memory bandwidth).
static constexpr double kStickyGcThroughputAdjustment = 1.0;
-// Whether or not we use the free list large object space.
+// Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR
+// since this means that we have to use the slow msync loop in MemMap::MapAnonymous.
+#if USE_ART_LOW_4G_ALLOCATOR
+static constexpr bool kUseFreeListSpaceForLOS = true;
+#else
static constexpr bool kUseFreeListSpaceForLOS = false;
+#endif
// Whether or not we compact the zygote in PreZygoteFork.
static constexpr bool kCompactZygote = kMovingCollector;
// How many reserve entries are at the end of the allocation stack, these are only needed if the
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 822af24..351e1c6 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -131,7 +131,6 @@
static constexpr bool kMeasureAllocationTime = false;
// Primitive arrays larger than this size are put in the large object space.
static constexpr size_t kDefaultLargeObjectThreshold = 3 * kPageSize;
-
static constexpr size_t kDefaultStartingSize = kPageSize;
static constexpr size_t kDefaultInitialSize = 2 * MB;
static constexpr size_t kDefaultMaximumSize = 256 * MB;
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index d5a03c6..2a43712 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -192,6 +192,96 @@
}
}
+// Keeps track of allocation sizes + whether or not the previous allocation is free.
+// Used to coalesce free blocks and find the best fit block for an allocation.
+class AllocationInfo {
+ public:
+ AllocationInfo() : prev_free_(0), alloc_size_(0) {
+ }
+ // Return the number of pages that the allocation info covers.
+ size_t AlignSize() const {
+ return alloc_size_ & ~kFlagFree;
+ }
+ // Returns the allocation size in bytes.
+ size_t ByteSize() const {
+ return AlignSize() * FreeListSpace::kAlignment;
+ }
+ // Updates the allocation size and whether or not it is free.
+ void SetByteSize(size_t size, bool free) {
+ DCHECK_ALIGNED(size, FreeListSpace::kAlignment);
+ alloc_size_ = (size / FreeListSpace::kAlignment) | (free ? kFlagFree : 0U);
+ }
+ bool IsFree() const {
+ return (alloc_size_ & kFlagFree) != 0;
+ }
+ // Finds and returns the next non free allocation info after ourself.
+ AllocationInfo* GetNextInfo() {
+ return this + AlignSize();
+ }
+ const AllocationInfo* GetNextInfo() const {
+ return this + AlignSize();
+ }
+ // Returns the previous free allocation info by using the prev_free_ member to figure out
+ // where it is. This is only used for coalescing so we only need to be able to do it if the
+ // previous allocation info is free.
+ AllocationInfo* GetPrevFreeInfo() {
+ DCHECK_NE(prev_free_, 0U);
+ return this - prev_free_;
+ }
+ // Returns the address of the object associated with this allocation info.
+ mirror::Object* GetObjectAddress() {
+ return reinterpret_cast<mirror::Object*>(reinterpret_cast<uintptr_t>(this) + sizeof(*this));
+ }
+ // Return how many kAlignment units there are before the free block.
+ size_t GetPrevFree() const {
+ return prev_free_;
+ }
+ // Returns how many free bytes there is before the block.
+ size_t GetPrevFreeBytes() const {
+ return GetPrevFree() * FreeListSpace::kAlignment;
+ }
+ // Update the size of the free block prior to the allocation.
+ void SetPrevFreeBytes(size_t bytes) {
+ DCHECK_ALIGNED(bytes, FreeListSpace::kAlignment);
+ prev_free_ = bytes / FreeListSpace::kAlignment;
+ }
+
+ private:
+ // Used to implement best fit object allocation. Each allocation has an AllocationInfo which
+ // contains the size of the previous free block preceding it. Implemented in such a way that we
+ // can also find the iterator for any allocation info pointer.
+ static constexpr uint32_t kFlagFree = 0x8000000;
+ // Contains the size of the previous free block with kAlignment as the unit. If 0 then the
+ // allocation before us is not free.
+ // These variables are undefined in the middle of allocations / free blocks.
+ uint32_t prev_free_;
+ // Allocation size of this object in kAlignment as the unit.
+ uint32_t alloc_size_;
+};
+
+size_t FreeListSpace::GetSlotIndexForAllocationInfo(const AllocationInfo* info) const {
+ DCHECK_GE(info, allocation_info_);
+ DCHECK_LT(info, reinterpret_cast<AllocationInfo*>(allocation_info_map_->End()));
+ return info - allocation_info_;
+}
+
+AllocationInfo* FreeListSpace::GetAllocationInfoForAddress(uintptr_t address) {
+ return &allocation_info_[GetSlotIndexForAddress(address)];
+}
+
+const AllocationInfo* FreeListSpace::GetAllocationInfoForAddress(uintptr_t address) const {
+ return &allocation_info_[GetSlotIndexForAddress(address)];
+}
+
+inline bool FreeListSpace::SortByPrevFree::operator()(const AllocationInfo* a,
+ const AllocationInfo* b) const {
+ if (a->GetPrevFree() < b->GetPrevFree()) return true;
+ if (a->GetPrevFree() > b->GetPrevFree()) return false;
+ if (a->AlignSize() < b->AlignSize()) return true;
+ if (a->AlignSize() > b->AlignSize()) return false;
+ return reinterpret_cast<uintptr_t>(a) < reinterpret_cast<uintptr_t>(b);
+}
+
FreeListSpace* FreeListSpace::Create(const std::string& name, byte* requested_begin, size_t size) {
CHECK_EQ(size % kAlignment, 0U);
std::string error_msg;
@@ -205,112 +295,113 @@
: LargeObjectSpace(name, begin, end),
mem_map_(mem_map),
lock_("free list space lock", kAllocSpaceLock) {
- free_end_ = end - begin;
+ const size_t space_capacity = end - begin;
+ free_end_ = space_capacity;
+ CHECK_ALIGNED(space_capacity, kAlignment);
+ const size_t alloc_info_size = sizeof(AllocationInfo) * (space_capacity / kAlignment);
+ std::string error_msg;
+ allocation_info_map_.reset(MemMap::MapAnonymous("large object free list space allocation info map",
+ nullptr, alloc_info_size, PROT_READ | PROT_WRITE,
+ false, &error_msg));
+ CHECK(allocation_info_map_.get() != nullptr) << "Failed to allocate allocation info map"
+ << error_msg;
+ allocation_info_ = reinterpret_cast<AllocationInfo*>(allocation_info_map_->Begin());
}
FreeListSpace::~FreeListSpace() {}
void FreeListSpace::Walk(DlMallocSpace::WalkCallback callback, void* arg) {
MutexLock mu(Thread::Current(), lock_);
- uintptr_t free_end_start = reinterpret_cast<uintptr_t>(end_) - free_end_;
- AllocationHeader* cur_header = reinterpret_cast<AllocationHeader*>(Begin());
- while (reinterpret_cast<uintptr_t>(cur_header) < free_end_start) {
- cur_header = cur_header->GetNextNonFree();
- size_t alloc_size = cur_header->AllocationSize();
- byte* byte_start = reinterpret_cast<byte*>(cur_header->GetObjectAddress());
- byte* byte_end = byte_start + alloc_size - sizeof(AllocationHeader);
- callback(byte_start, byte_end, alloc_size, arg);
- callback(NULL, NULL, 0, arg);
- cur_header = reinterpret_cast<AllocationHeader*>(byte_end);
+ const uintptr_t free_end_start = reinterpret_cast<uintptr_t>(end_) - free_end_;
+ AllocationInfo* cur_info = &allocation_info_[0];
+ const AllocationInfo* end_info = GetAllocationInfoForAddress(free_end_start);
+ while (cur_info < end_info) {
+ if (!cur_info->IsFree()) {
+ size_t alloc_size = cur_info->ByteSize();
+ byte* byte_start = reinterpret_cast<byte*>(GetAddressForAllocationInfo(cur_info));
+ byte* byte_end = byte_start + alloc_size;
+ callback(byte_start, byte_end, alloc_size, arg);
+ callback(nullptr, nullptr, 0, arg);
+ }
+ cur_info = cur_info->GetNextInfo();
}
+ CHECK_EQ(cur_info, end_info);
}
-void FreeListSpace::RemoveFreePrev(AllocationHeader* header) {
- CHECK(!header->IsFree());
- CHECK_GT(header->GetPrevFree(), size_t(0));
- FreeBlocks::iterator found = free_blocks_.lower_bound(header);
- CHECK(found != free_blocks_.end());
- CHECK_EQ(*found, header);
- free_blocks_.erase(found);
-}
-
-FreeListSpace::AllocationHeader* FreeListSpace::GetAllocationHeader(const mirror::Object* obj) {
- DCHECK(Contains(obj));
- return reinterpret_cast<AllocationHeader*>(reinterpret_cast<uintptr_t>(obj) -
- sizeof(AllocationHeader));
-}
-
-FreeListSpace::AllocationHeader* FreeListSpace::AllocationHeader::GetNextNonFree() {
- // We know that there has to be at least one object after us or else we would have
- // coalesced with the free end region. May be worth investigating a better way to do this
- // as it may be expensive for large allocations.
- for (uintptr_t pos = reinterpret_cast<uintptr_t>(this);; pos += kAlignment) {
- AllocationHeader* cur = reinterpret_cast<AllocationHeader*>(pos);
- if (!cur->IsFree()) return cur;
- }
+void FreeListSpace::RemoveFreePrev(AllocationInfo* info) {
+ CHECK_GT(info->GetPrevFree(), 0U);
+ auto it = free_blocks_.lower_bound(info);
+ CHECK(it != free_blocks_.end());
+ CHECK_EQ(*it, info);
+ free_blocks_.erase(it);
}
size_t FreeListSpace::Free(Thread* self, mirror::Object* obj) {
MutexLock mu(self, lock_);
- DCHECK(Contains(obj));
- AllocationHeader* header = GetAllocationHeader(obj);
- CHECK(IsAligned<kAlignment>(header));
- size_t allocation_size = header->AllocationSize();
- DCHECK_GT(allocation_size, size_t(0));
- DCHECK(IsAligned<kAlignment>(allocation_size));
+ DCHECK(Contains(obj)) << reinterpret_cast<void*>(Begin()) << " " << obj << " "
+ << reinterpret_cast<void*>(End());
+ DCHECK_ALIGNED(obj, kAlignment);
+ AllocationInfo* info = GetAllocationInfoForAddress(reinterpret_cast<uintptr_t>(obj));
+ DCHECK(!info->IsFree());
+ const size_t allocation_size = info->ByteSize();
+ DCHECK_GT(allocation_size, 0U);
+ DCHECK_ALIGNED(allocation_size, kAlignment);
+ info->SetByteSize(allocation_size, true); // Mark as free.
// Look at the next chunk.
- AllocationHeader* next_header = header->GetNextAllocationHeader();
+ AllocationInfo* next_info = info->GetNextInfo();
// Calculate the start of the end free block.
uintptr_t free_end_start = reinterpret_cast<uintptr_t>(end_) - free_end_;
- size_t header_prev_free = header->GetPrevFree();
+ size_t prev_free_bytes = info->GetPrevFreeBytes();
size_t new_free_size = allocation_size;
- if (header_prev_free) {
- new_free_size += header_prev_free;
- RemoveFreePrev(header);
+ if (prev_free_bytes != 0) {
+ // Coalesce with previous free chunk.
+ new_free_size += prev_free_bytes;
+ RemoveFreePrev(info);
+ info = info->GetPrevFreeInfo();
+ // The previous allocation info must not be free since we are supposed to always coalesce.
+ DCHECK_EQ(info->GetPrevFreeBytes(), 0U) << "Previous allocation was free";
}
- if (reinterpret_cast<uintptr_t>(next_header) >= free_end_start) {
+ uintptr_t next_addr = GetAddressForAllocationInfo(next_info);
+ if (next_addr >= free_end_start) {
// Easy case, the next chunk is the end free region.
- CHECK_EQ(reinterpret_cast<uintptr_t>(next_header), free_end_start);
+ CHECK_EQ(next_addr, free_end_start);
free_end_ += new_free_size;
} else {
- AllocationHeader* new_free_header;
- DCHECK(IsAligned<kAlignment>(next_header));
- if (next_header->IsFree()) {
- // Find the next chunk by reading each page until we hit one with non-zero chunk.
- AllocationHeader* next_next_header = next_header->GetNextNonFree();
- DCHECK(IsAligned<kAlignment>(next_next_header));
- DCHECK(IsAligned<kAlignment>(next_next_header->AllocationSize()));
- RemoveFreePrev(next_next_header);
- new_free_header = next_next_header;
- new_free_size += next_next_header->GetPrevFree();
+ AllocationInfo* new_free_info;
+ if (next_info->IsFree()) {
+ AllocationInfo* next_next_info = next_info->GetNextInfo();
+ // Next next info can't be free since we always coalesce.
+ DCHECK(!next_next_info->IsFree());
+ DCHECK(IsAligned<kAlignment>(next_next_info->ByteSize()));
+ new_free_info = next_next_info;
+ new_free_size += next_next_info->GetPrevFreeBytes();
+ RemoveFreePrev(next_next_info);
} else {
- new_free_header = next_header;
+ new_free_info = next_info;
}
- new_free_header->prev_free_ = new_free_size;
- free_blocks_.insert(new_free_header);
+ new_free_info->SetPrevFreeBytes(new_free_size);
+ free_blocks_.insert(new_free_info);
+ info->SetByteSize(new_free_size, true);
+ DCHECK_EQ(info->GetNextInfo(), new_free_info);
}
--num_objects_allocated_;
DCHECK_LE(allocation_size, num_bytes_allocated_);
num_bytes_allocated_ -= allocation_size;
- madvise(header, allocation_size, MADV_DONTNEED);
+ madvise(obj, allocation_size, MADV_DONTNEED);
if (kIsDebugBuild) {
// Can't disallow reads since we use them to find next chunks during coalescing.
- mprotect(header, allocation_size, PROT_READ);
+ mprotect(obj, allocation_size, PROT_READ);
}
return allocation_size;
}
-bool FreeListSpace::Contains(const mirror::Object* obj) const {
- return mem_map_->HasAddress(obj);
-}
-
size_t FreeListSpace::AllocationSize(mirror::Object* obj, size_t* usable_size) {
- AllocationHeader* header = GetAllocationHeader(obj);
DCHECK(Contains(obj));
- DCHECK(!header->IsFree());
- size_t alloc_size = header->AllocationSize();
+ AllocationInfo* info = GetAllocationInfoForAddress(reinterpret_cast<uintptr_t>(obj));
+ DCHECK(!info->IsFree());
+ size_t alloc_size = info->ByteSize();
if (usable_size != nullptr) {
- *usable_size = alloc_size - sizeof(AllocationHeader);
+ *usable_size = alloc_size;
}
return alloc_size;
}
@@ -318,56 +409,56 @@
mirror::Object* FreeListSpace::Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
size_t* usable_size) {
MutexLock mu(self, lock_);
- size_t allocation_size = RoundUp(num_bytes + sizeof(AllocationHeader), kAlignment);
- AllocationHeader temp;
- temp.SetPrevFree(allocation_size);
- temp.SetAllocationSize(0);
- AllocationHeader* new_header;
+ const size_t allocation_size = RoundUp(num_bytes, kAlignment);
+ AllocationInfo temp_info;
+ temp_info.SetPrevFreeBytes(allocation_size);
+ temp_info.SetByteSize(0, false);
+ AllocationInfo* new_info;
// Find the smallest chunk at least num_bytes in size.
- FreeBlocks::iterator found = free_blocks_.lower_bound(&temp);
- if (found != free_blocks_.end()) {
- AllocationHeader* header = *found;
- free_blocks_.erase(found);
-
- // Fit our object in the previous free header space.
- new_header = header->GetPrevFreeAllocationHeader();
-
- // Remove the newly allocated block from the header and update the prev_free_.
- header->prev_free_ -= allocation_size;
- if (header->prev_free_ > 0) {
+ auto it = free_blocks_.lower_bound(&temp_info);
+ if (it != free_blocks_.end()) {
+ AllocationInfo* info = *it;
+ free_blocks_.erase(it);
+ // Fit our object in the previous allocation info free space.
+ new_info = info->GetPrevFreeInfo();
+ // Remove the newly allocated block from the info and update the prev_free_.
+ info->SetPrevFreeBytes(info->GetPrevFreeBytes() - allocation_size);
+ if (info->GetPrevFreeBytes() > 0) {
+ AllocationInfo* new_free = info - info->GetPrevFree();
+ new_free->SetPrevFreeBytes(0);
+ new_free->SetByteSize(info->GetPrevFreeBytes(), true);
// If there is remaining space, insert back into the free set.
- free_blocks_.insert(header);
+ free_blocks_.insert(info);
}
} else {
// Try to steal some memory from the free space at the end of the space.
if (LIKELY(free_end_ >= allocation_size)) {
// Fit our object at the start of the end free block.
- new_header = reinterpret_cast<AllocationHeader*>(end_ - free_end_);
+ new_info = GetAllocationInfoForAddress(reinterpret_cast<uintptr_t>(End()) - free_end_);
free_end_ -= allocation_size;
} else {
return nullptr;
}
}
-
DCHECK(bytes_allocated != nullptr);
*bytes_allocated = allocation_size;
if (usable_size != nullptr) {
- *usable_size = allocation_size - sizeof(AllocationHeader);
+ *usable_size = allocation_size;
}
// Need to do these inside of the lock.
++num_objects_allocated_;
++total_objects_allocated_;
num_bytes_allocated_ += allocation_size;
total_bytes_allocated_ += allocation_size;
-
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(GetAddressForAllocationInfo(new_info));
// We always put our object at the start of the free block, there can not be another free block
// before it.
if (kIsDebugBuild) {
- mprotect(new_header, allocation_size, PROT_READ | PROT_WRITE);
+ mprotect(obj, allocation_size, PROT_READ | PROT_WRITE);
}
- new_header->SetPrevFree(0);
- new_header->SetAllocationSize(allocation_size);
- return new_header->GetObjectAddress();
+ new_info->SetPrevFreeBytes(0);
+ new_info->SetByteSize(allocation_size, false);
+ return obj;
}
void FreeListSpace::Dump(std::ostream& os) const {
@@ -376,21 +467,20 @@
<< " begin: " << reinterpret_cast<void*>(Begin())
<< " end: " << reinterpret_cast<void*>(End()) << "\n";
uintptr_t free_end_start = reinterpret_cast<uintptr_t>(end_) - free_end_;
- AllocationHeader* cur_header = reinterpret_cast<AllocationHeader*>(Begin());
- while (reinterpret_cast<uintptr_t>(cur_header) < free_end_start) {
- byte* free_start = reinterpret_cast<byte*>(cur_header);
- cur_header = cur_header->GetNextNonFree();
- byte* free_end = reinterpret_cast<byte*>(cur_header);
- if (free_start != free_end) {
- os << "Free block at address: " << reinterpret_cast<const void*>(free_start)
- << " of length " << free_end - free_start << " bytes\n";
+ const AllocationInfo* cur_info =
+ GetAllocationInfoForAddress(reinterpret_cast<uintptr_t>(Begin()));
+ const AllocationInfo* end_info = GetAllocationInfoForAddress(free_end_start);
+ while (cur_info < end_info) {
+ size_t size = cur_info->ByteSize();
+ uintptr_t address = GetAddressForAllocationInfo(cur_info);
+ if (cur_info->IsFree()) {
+ os << "Free block at address: " << reinterpret_cast<const void*>(address)
+ << " of length " << size << " bytes\n";
+ } else {
+ os << "Large object at address: " << reinterpret_cast<const void*>(address)
+ << " of length " << size << " bytes\n";
}
- size_t alloc_size = cur_header->AllocationSize();
- byte* byte_start = reinterpret_cast<byte*>(cur_header->GetObjectAddress());
- byte* byte_end = byte_start + alloc_size - sizeof(AllocationHeader);
- os << "Large object at address: " << reinterpret_cast<const void*>(free_start)
- << " of length " << byte_end - byte_start << " bytes\n";
- cur_header = reinterpret_cast<AllocationHeader*>(byte_end);
+ cur_info = cur_info->GetNextInfo();
}
if (free_end_) {
os << "Free block at address: " << reinterpret_cast<const void*>(free_end_start)
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index 09a0919..9d5e090 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -29,13 +29,14 @@
namespace gc {
namespace space {
+class AllocationInfo;
+
// Abstraction implemented by all large object spaces.
class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace {
public:
SpaceType GetType() const OVERRIDE {
return kSpaceTypeLargeObjectSpace;
}
-
void SwapBitmaps();
void CopyLiveToMarked();
virtual void Walk(DlMallocSpace::WalkCallback, void* arg) = 0;
@@ -44,57 +45,53 @@
uint64_t GetBytesAllocated() OVERRIDE {
return num_bytes_allocated_;
}
-
uint64_t GetObjectsAllocated() OVERRIDE {
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, mirror::Object** ptrs) OVERRIDE;
-
// LargeObjectSpaces don't have thread local state.
void RevokeThreadLocalBuffers(art::Thread*) OVERRIDE {
}
void RevokeAllThreadLocalBuffers() OVERRIDE {
}
-
bool IsAllocSpace() const OVERRIDE {
return true;
}
-
AllocSpace* AsAllocSpace() OVERRIDE {
return this;
}
-
collector::ObjectBytePair Sweep(bool swap_bitmaps);
-
virtual bool CanMoveObjects() const OVERRIDE {
return false;
}
-
// Current address at which the space begins, which may vary as the space is filled.
byte* Begin() const {
return begin_;
}
-
// Current 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();
+ }
+ // Return true if we contain the specified address.
+ bool Contains(const mirror::Object* obj) const {
+ const byte* byte_obj = reinterpret_cast<const byte*>(obj);
+ return Begin() <= byte_obj && byte_obj < End();
+ }
void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
protected:
explicit LargeObjectSpace(const std::string& name, byte* begin, byte* end);
-
static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg);
// Approximate number of bytes which have been allocated into the space.
@@ -102,7 +99,6 @@
uint64_t num_objects_allocated_;
uint64_t total_bytes_allocated_;
uint64_t total_objects_allocated_;
-
// Begin and end, may change as more large objects are allocated.
byte* begin_;
byte* end_;
@@ -119,7 +115,6 @@
// 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.
size_t AllocationSize(mirror::Object* obj, size_t* usable_size);
mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
@@ -145,126 +140,52 @@
// A continuous large object space with a free-list to handle holes.
class FreeListSpace FINAL : public LargeObjectSpace {
public:
+ static constexpr size_t kAlignment = kPageSize;
+
virtual ~FreeListSpace();
static FreeListSpace* Create(const std::string& name, byte* requested_begin, size_t capacity);
-
size_t AllocationSize(mirror::Object* obj, size_t* usable_size) OVERRIDE
EXCLUSIVE_LOCKS_REQUIRED(lock_);
mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
size_t* usable_size) OVERRIDE;
size_t Free(Thread* self, mirror::Object* obj) OVERRIDE;
- bool Contains(const mirror::Object* obj) const OVERRIDE;
void Walk(DlMallocSpace::WalkCallback callback, void* arg) OVERRIDE LOCKS_EXCLUDED(lock_);
-
- // 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();
- }
-
void Dump(std::ostream& os) const;
protected:
- static const size_t kAlignment = kPageSize;
-
- class AllocationHeader {
- public:
- // Returns the allocation size, includes the header.
- size_t AllocationSize() const {
- return alloc_size_;
- }
-
- // Updates the allocation size in the header, the allocation size includes the header itself.
- void SetAllocationSize(size_t size) {
- DCHECK(IsAligned<kPageSize>(size));
- alloc_size_ = size;
- }
-
- bool IsFree() const {
- return AllocationSize() == 0;
- }
-
- // Returns the previous free allocation header by using the prev_free_ member to figure out
- // where it is. If prev free is 0 then we just return ourself.
- AllocationHeader* GetPrevFreeAllocationHeader() {
- return reinterpret_cast<AllocationHeader*>(reinterpret_cast<uintptr_t>(this) - prev_free_);
- }
-
- // Returns the address of the object associated with this allocation header.
- mirror::Object* GetObjectAddress() {
- return reinterpret_cast<mirror::Object*>(reinterpret_cast<uintptr_t>(this) + sizeof(*this));
- }
-
- // Returns the next allocation header after the object associated with this allocation header.
- AllocationHeader* GetNextAllocationHeader() {
- DCHECK_NE(alloc_size_, 0U);
- return reinterpret_cast<AllocationHeader*>(reinterpret_cast<uintptr_t>(this) + alloc_size_);
- }
-
- // Returns how many free bytes there is before the block.
- size_t GetPrevFree() const {
- return prev_free_;
- }
-
- // Update the size of the free block prior to the allocation.
- void SetPrevFree(size_t prev_free) {
- DCHECK(IsAligned<kPageSize>(prev_free));
- prev_free_ = prev_free;
- }
-
- // Finds and returns the next non free allocation header after ourself.
- // TODO: Optimize, currently O(n) for n free following pages.
- AllocationHeader* GetNextNonFree();
-
- // Used to implement best fit object allocation. Each allocation has an AllocationHeader which
- // contains the size of the previous free block preceding it. Implemented in such a way that we
- // can also find the iterator for any allocation header pointer.
- class SortByPrevFree {
- public:
- bool operator()(const AllocationHeader* a, const AllocationHeader* b) const {
- if (a->GetPrevFree() < b->GetPrevFree()) return true;
- if (a->GetPrevFree() > b->GetPrevFree()) return false;
- if (a->AllocationSize() < b->AllocationSize()) return true;
- if (a->AllocationSize() > b->AllocationSize()) return false;
- return reinterpret_cast<uintptr_t>(a) < reinterpret_cast<uintptr_t>(b);
- }
- };
-
- private:
- // Contains the size of the previous free block, if 0 then the memory preceding us is an
- // allocation.
- size_t prev_free_;
-
- // Allocation size of this object, 0 means that the allocation header is free memory.
- size_t alloc_size_;
-
- friend class FreeListSpace;
- };
-
FreeListSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end);
-
+ size_t GetSlotIndexForAddress(uintptr_t address) const {
+ DCHECK(Contains(reinterpret_cast<mirror::Object*>(address)));
+ return (address - reinterpret_cast<uintptr_t>(Begin())) / kAlignment;
+ }
+ size_t GetSlotIndexForAllocationInfo(const AllocationInfo* info) const;
+ AllocationInfo* GetAllocationInfoForAddress(uintptr_t address);
+ const AllocationInfo* GetAllocationInfoForAddress(uintptr_t address) const;
+ uintptr_t GetAllocationAddressForSlot(size_t slot) const {
+ return reinterpret_cast<uintptr_t>(Begin()) + slot * kAlignment;
+ }
+ uintptr_t GetAddressForAllocationInfo(const AllocationInfo* info) const {
+ return GetAllocationAddressForSlot(GetSlotIndexForAllocationInfo(info));
+ }
// Removes header from the free blocks set by finding the corresponding iterator and erasing it.
- void RemoveFreePrev(AllocationHeader* header) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+ void RemoveFreePrev(AllocationInfo* info) EXCLUSIVE_LOCKS_REQUIRED(lock_);
- // Finds the allocation header corresponding to obj.
- AllocationHeader* GetAllocationHeader(const mirror::Object* obj);
-
- typedef std::set<AllocationHeader*, AllocationHeader::SortByPrevFree,
- TrackingAllocator<AllocationHeader*, kAllocatorTagLOSFreeList>> FreeBlocks;
+ class SortByPrevFree {
+ public:
+ bool operator()(const AllocationInfo* a, const AllocationInfo* b) const;
+ };
+ typedef std::set<AllocationInfo*, SortByPrevFree,
+ TrackingAllocator<AllocationInfo*, kAllocatorTagLOSFreeList>> FreeBlocks;
// There is not footer for any allocations at the end of the space, so we keep track of how much
// free space there is at the end manually.
std::unique_ptr<MemMap> mem_map_;
+ // Side table for allocation info, one per page.
+ std::unique_ptr<MemMap> allocation_info_map_;
+ AllocationInfo* allocation_info_;
+
Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // Free bytes at the end of the space.
size_t free_end_ GUARDED_BY(lock_);
FreeBlocks free_blocks_ GUARDED_BY(lock_);
};
diff --git a/runtime/gc/space/large_object_space_test.cc b/runtime/gc/space/large_object_space_test.cc
index f733584..c5d8abc 100644
--- a/runtime/gc/space/large_object_space_test.cc
+++ b/runtime/gc/space/large_object_space_test.cc
@@ -80,6 +80,8 @@
ASSERT_GE(los->Free(Thread::Current(), obj), request_size);
}
}
+ // Test that dump doesn't crash.
+ los->Dump(LOG(INFO));
size_t bytes_allocated = 0;
// Checks that the coalescing works.
diff --git a/test/083-compiler-regressions/expected.txt b/test/083-compiler-regressions/expected.txt
index f8d92cc..e907fd1 100644
--- a/test/083-compiler-regressions/expected.txt
+++ b/test/083-compiler-regressions/expected.txt
@@ -1,3 +1,4 @@
+b17411468 passes
b2296099 passes
b2302318 passes
b2487514 passes
diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java
index c089c52..8d7bf01 100644
--- a/test/083-compiler-regressions/src/Main.java
+++ b/test/083-compiler-regressions/src/Main.java
@@ -30,6 +30,7 @@
}
public static void main(String args[]) throws Exception {
+ b17411468();
b2296099Test();
b2302318Test();
b2487514Test();
@@ -61,6 +62,17 @@
minDoubleWith3ConstsTest();
}
+ public static void b17411468() {
+ // b/17411468 - inline Math.round failure.
+ double d1 = 1.0;
+ double d2 = Math.round(d1);
+ if (d1 == d2) {
+ System.out.println("b17411468 passes");
+ } else {
+ System.out.println("b17411468 fails: Math.round(" + d1 + ") returned " + d2);
+ }
+ }
+
public static double minDouble(double a, double b, double c) {
return Math.min(Math.min(a, b), c);
}