Home | History | Annotate | Download | only in space
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "bump_pointer_space.h"
     18 #include "bump_pointer_space-inl.h"
     19 #include "mirror/class-inl.h"
     20 #include "mirror/object-inl.h"
     21 #include "thread_list.h"
     22 
     23 namespace art {
     24 namespace gc {
     25 namespace space {
     26 
     27 BumpPointerSpace* BumpPointerSpace::Create(const std::string& name, size_t capacity) {
     28   capacity = RoundUp(capacity, kPageSize);
     29   std::string error_msg;
     30   MemMap mem_map = MemMap::MapAnonymous(name.c_str(),
     31                                         capacity,
     32                                         PROT_READ | PROT_WRITE,
     33                                         /*low_4gb=*/ true,
     34                                         &error_msg);
     35   if (!mem_map.IsValid()) {
     36     LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size "
     37         << PrettySize(capacity) << " with message " << error_msg;
     38     return nullptr;
     39   }
     40   return new BumpPointerSpace(name, std::move(mem_map));
     41 }
     42 
     43 BumpPointerSpace* BumpPointerSpace::CreateFromMemMap(const std::string& name, MemMap&& mem_map) {
     44   return new BumpPointerSpace(name, std::move(mem_map));
     45 }
     46 
     47 BumpPointerSpace::BumpPointerSpace(const std::string& name, uint8_t* begin, uint8_t* limit)
     48     : ContinuousMemMapAllocSpace(name,
     49                                  MemMap::Invalid(),
     50                                  begin,
     51                                  begin,
     52                                  limit,
     53                                  kGcRetentionPolicyAlwaysCollect),
     54       growth_end_(limit),
     55       objects_allocated_(0), bytes_allocated_(0),
     56       block_lock_("Block lock"),
     57       main_block_size_(0),
     58       num_blocks_(0) {
     59 }
     60 
     61 BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap&& mem_map)
     62     : ContinuousMemMapAllocSpace(name,
     63                                  std::move(mem_map),
     64                                  mem_map.Begin(),
     65                                  mem_map.Begin(),
     66                                  mem_map.End(),
     67                                  kGcRetentionPolicyAlwaysCollect),
     68       growth_end_(mem_map_.End()),
     69       objects_allocated_(0), bytes_allocated_(0),
     70       block_lock_("Block lock", kBumpPointerSpaceBlockLock),
     71       main_block_size_(0),
     72       num_blocks_(0) {
     73 }
     74 
     75 void BumpPointerSpace::Clear() {
     76   // Release the pages back to the operating system.
     77   if (!kMadviseZeroes) {
     78     memset(Begin(), 0, Limit() - Begin());
     79   }
     80   CHECK_NE(madvise(Begin(), Limit() - Begin(), MADV_DONTNEED), -1) << "madvise failed";
     81   // Reset the end of the space back to the beginning, we move the end forward as we allocate
     82   // objects.
     83   SetEnd(Begin());
     84   objects_allocated_.store(0, std::memory_order_relaxed);
     85   bytes_allocated_.store(0, std::memory_order_relaxed);
     86   growth_end_ = Limit();
     87   {
     88     MutexLock mu(Thread::Current(), block_lock_);
     89     num_blocks_ = 0;
     90     main_block_size_ = 0;
     91   }
     92 }
     93 
     94 void BumpPointerSpace::Dump(std::ostream& os) const {
     95   os << GetName() << " "
     96       << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(End()) << " - "
     97       << reinterpret_cast<void*>(Limit());
     98 }
     99 
    100 mirror::Object* BumpPointerSpace::GetNextObject(mirror::Object* obj) {
    101   const uintptr_t position = reinterpret_cast<uintptr_t>(obj) + obj->SizeOf();
    102   return reinterpret_cast<mirror::Object*>(RoundUp(position, kAlignment));
    103 }
    104 
    105 size_t BumpPointerSpace::RevokeThreadLocalBuffers(Thread* thread) {
    106   MutexLock mu(Thread::Current(), block_lock_);
    107   RevokeThreadLocalBuffersLocked(thread);
    108   return 0U;
    109 }
    110 
    111 size_t BumpPointerSpace::RevokeAllThreadLocalBuffers() {
    112   Thread* self = Thread::Current();
    113   MutexLock mu(self, *Locks::runtime_shutdown_lock_);
    114   MutexLock mu2(self, *Locks::thread_list_lock_);
    115   // TODO: Not do a copy of the thread list?
    116   std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
    117   for (Thread* thread : thread_list) {
    118     RevokeThreadLocalBuffers(thread);
    119   }
    120   return 0U;
    121 }
    122 
    123 void BumpPointerSpace::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
    124   if (kIsDebugBuild) {
    125     MutexLock mu(Thread::Current(), block_lock_);
    126     DCHECK(!thread->HasTlab());
    127   }
    128 }
    129 
    130 void BumpPointerSpace::AssertAllThreadLocalBuffersAreRevoked() {
    131   if (kIsDebugBuild) {
    132     Thread* self = Thread::Current();
    133     MutexLock mu(self, *Locks::runtime_shutdown_lock_);
    134     MutexLock mu2(self, *Locks::thread_list_lock_);
    135     // TODO: Not do a copy of the thread list?
    136     std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
    137     for (Thread* thread : thread_list) {
    138       AssertThreadLocalBuffersAreRevoked(thread);
    139     }
    140   }
    141 }
    142 
    143 void BumpPointerSpace::UpdateMainBlock() {
    144   DCHECK_EQ(num_blocks_, 0U);
    145   main_block_size_ = Size();
    146 }
    147 
    148 // Returns the start of the storage.
    149 uint8_t* BumpPointerSpace::AllocBlock(size_t bytes) {
    150   bytes = RoundUp(bytes, kAlignment);
    151   if (!num_blocks_) {
    152     UpdateMainBlock();
    153   }
    154   uint8_t* storage = reinterpret_cast<uint8_t*>(
    155       AllocNonvirtualWithoutAccounting(bytes + sizeof(BlockHeader)));
    156   if (LIKELY(storage != nullptr)) {
    157     BlockHeader* header = reinterpret_cast<BlockHeader*>(storage);
    158     header->size_ = bytes;  // Write out the block header.
    159     storage += sizeof(BlockHeader);
    160     ++num_blocks_;
    161   }
    162   return storage;
    163 }
    164 
    165 accounting::ContinuousSpaceBitmap::SweepCallback* BumpPointerSpace::GetSweepCallback() {
    166   UNIMPLEMENTED(FATAL);
    167   UNREACHABLE();
    168 }
    169 
    170 uint64_t BumpPointerSpace::GetBytesAllocated() {
    171   // Start out pre-determined amount (blocks which are not being allocated into).
    172   uint64_t total = static_cast<uint64_t>(bytes_allocated_.load(std::memory_order_relaxed));
    173   Thread* self = Thread::Current();
    174   MutexLock mu(self, *Locks::runtime_shutdown_lock_);
    175   MutexLock mu2(self, *Locks::thread_list_lock_);
    176   std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
    177   MutexLock mu3(Thread::Current(), block_lock_);
    178   // If we don't have any blocks, we don't have any thread local buffers. This check is required
    179   // since there can exist multiple bump pointer spaces which exist at the same time.
    180   if (num_blocks_ > 0) {
    181     for (Thread* thread : thread_list) {
    182       total += thread->GetThreadLocalBytesAllocated();
    183     }
    184   }
    185   return total;
    186 }
    187 
    188 uint64_t BumpPointerSpace::GetObjectsAllocated() {
    189   // Start out pre-determined amount (blocks which are not being allocated into).
    190   uint64_t total = static_cast<uint64_t>(objects_allocated_.load(std::memory_order_relaxed));
    191   Thread* self = Thread::Current();
    192   MutexLock mu(self, *Locks::runtime_shutdown_lock_);
    193   MutexLock mu2(self, *Locks::thread_list_lock_);
    194   std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
    195   MutexLock mu3(Thread::Current(), block_lock_);
    196   // If we don't have any blocks, we don't have any thread local buffers. This check is required
    197   // since there can exist multiple bump pointer spaces which exist at the same time.
    198   if (num_blocks_ > 0) {
    199     for (Thread* thread : thread_list) {
    200       total += thread->GetThreadLocalObjectsAllocated();
    201     }
    202   }
    203   return total;
    204 }
    205 
    206 void BumpPointerSpace::RevokeThreadLocalBuffersLocked(Thread* thread) {
    207   objects_allocated_.fetch_add(thread->GetThreadLocalObjectsAllocated(), std::memory_order_relaxed);
    208   bytes_allocated_.fetch_add(thread->GetThreadLocalBytesAllocated(), std::memory_order_relaxed);
    209   thread->SetTlab(nullptr, nullptr, nullptr);
    210 }
    211 
    212 bool BumpPointerSpace::AllocNewTlab(Thread* self, size_t bytes) {
    213   MutexLock mu(Thread::Current(), block_lock_);
    214   RevokeThreadLocalBuffersLocked(self);
    215   uint8_t* start = AllocBlock(bytes);
    216   if (start == nullptr) {
    217     return false;
    218   }
    219   self->SetTlab(start, start + bytes, start + bytes);
    220   return true;
    221 }
    222 
    223 void BumpPointerSpace::LogFragmentationAllocFailure(std::ostream& os,
    224                                                     size_t /* failed_alloc_bytes */) {
    225   size_t max_contiguous_allocation = Limit() - End();
    226   os << "; failed due to fragmentation (largest possible contiguous allocation "
    227      <<  max_contiguous_allocation << " bytes)";
    228   // Caller's job to print failed_alloc_bytes.
    229 }
    230 
    231 size_t BumpPointerSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable_size) {
    232   size_t num_bytes = obj->SizeOf();
    233   if (usable_size != nullptr) {
    234     *usable_size = RoundUp(num_bytes, kAlignment);
    235   }
    236   return num_bytes;
    237 }
    238 
    239 }  // namespace space
    240 }  // namespace gc
    241 }  // namespace art
    242