blob: b066dd593dc2f1d8def5144f3d981dd9cae93254 [file] [log] [blame]
/*
* 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);
}
void FreeListSpace::Dump(std::ostream& os) const{
os << GetName() << " -"
<< " begin: " << reinterpret_cast<void*>(Begin())
<< " end: " << reinterpret_cast<void*>(End());
}
}