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/object-inl.h"
     20 #include "mirror/class-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                                            uint8_t* requested_begin) {
     29   capacity = RoundUp(capacity, kPageSize);
     30   std::string error_msg;
     31   std::unique_ptr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), requested_begin, capacity,
     32                                                        PROT_READ | PROT_WRITE, true, false,
     33                                                        &error_msg));
     34   if (mem_map.get() == nullptr) {
     35     LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size "
     36         << PrettySize(capacity) << " with message " << error_msg;
     37     return nullptr;
     38   }
     39   return new BumpPointerSpace(name, mem_map.release());
     40 }
     41 
     42 BumpPointerSpace* BumpPointerSpace::CreateFromMemMap(const std::string& name, MemMap* mem_map) {
     43   return new BumpPointerSpace(name, mem_map);
     44 }
     45 
     46 BumpPointerSpace::BumpPointerSpace(const std::string& name, uint8_t* begin, uint8_t* limit)
     47     : ContinuousMemMapAllocSpace(name, nullptr, begin, begin, limit,
     48                                  kGcRetentionPolicyAlwaysCollect),
     49       growth_end_(limit),
     50       objects_allocated_(0), bytes_allocated_(0),
     51       block_lock_("Block lock"),
     52       main_block_size_(0),
     53       num_blocks_(0) {
     54 }
     55 
     56 BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap* mem_map)
     57     : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->Begin(), mem_map->End(),
     58                                  kGcRetentionPolicyAlwaysCollect),
     59       growth_end_(mem_map->End()),
     60       objects_allocated_(0), bytes_allocated_(0),
     61       block_lock_("Block lock", kBumpPointerSpaceBlockLock),
     62       main_block_size_(0),
     63       num_blocks_(0) {
     64 }
     65 
     66 void BumpPointerSpace::Clear() {
     67   // Release the pages back to the operating system.
     68   if (!kMadviseZeroes) {
     69     memset(Begin(), 0, Limit() - Begin());
     70   }
     71   CHECK_NE(madvise(Begin(), Limit() - Begin(), MADV_DONTNEED), -1) << "madvise failed";
     72   // Reset the end of the space back to the beginning, we move the end forward as we allocate
     73   // objects.
     74   SetEnd(Begin());
     75   objects_allocated_.StoreRelaxed(0);
     76   bytes_allocated_.StoreRelaxed(0);
     77   growth_end_ = Limit();
     78   {
     79     MutexLock mu(Thread::Current(), block_lock_);
     80     num_blocks_ = 0;
     81     main_block_size_ = 0;
     82   }
     83 }
     84 
     85 void BumpPointerSpace::Dump(std::ostream& os) const {
     86   os << GetName() << " "
     87       << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(End()) << " - "
     88       << reinterpret_cast<void*>(Limit());
     89 }
     90 
     91 mirror::Object* BumpPointerSpace::GetNextObject(mirror::Object* obj) {
     92   const uintptr_t position = reinterpret_cast<uintptr_t>(obj) + obj->SizeOf();
     93   return reinterpret_cast<mirror::Object*>(RoundUp(position, kAlignment));
     94 }
     95 
     96 size_t BumpPointerSpace::RevokeThreadLocalBuffers(Thread* thread) {
     97   MutexLock mu(Thread::Current(), block_lock_);
     98   RevokeThreadLocalBuffersLocked(thread);
     99   return 0U;
    100 }
    101 
    102 size_t BumpPointerSpace::RevokeAllThreadLocalBuffers() {
    103   Thread* self = Thread::Current();
    104   MutexLock mu(self, *Locks::runtime_shutdown_lock_);
    105   MutexLock mu2(self, *Locks::thread_list_lock_);
    106   // TODO: Not do a copy of the thread list?
    107   std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
    108   for (Thread* thread : thread_list) {
    109     RevokeThreadLocalBuffers(thread);
    110   }
    111   return 0U;
    112 }
    113 
    114 void BumpPointerSpace::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
    115   if (kIsDebugBuild) {
    116     MutexLock mu(Thread::Current(), block_lock_);
    117     DCHECK(!thread->HasTlab());
    118   }
    119 }
    120 
    121 void BumpPointerSpace::AssertAllThreadLocalBuffersAreRevoked() {
    122   if (kIsDebugBuild) {
    123     Thread* self = Thread::Current();
    124     MutexLock mu(self, *Locks::runtime_shutdown_lock_);
    125     MutexLock mu2(self, *Locks::thread_list_lock_);
    126     // TODO: Not do a copy of the thread list?
    127     std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
    128     for (Thread* thread : thread_list) {
    129       AssertThreadLocalBuffersAreRevoked(thread);
    130     }
    131   }
    132 }
    133 
    134 void BumpPointerSpace::UpdateMainBlock() {
    135   DCHECK_EQ(num_blocks_, 0U);
    136   main_block_size_ = Size();
    137 }
    138 
    139 // Returns the start of the storage.
    140 uint8_t* BumpPointerSpace::AllocBlock(size_t bytes) {
    141   bytes = RoundUp(bytes, kAlignment);
    142   if (!num_blocks_) {
    143     UpdateMainBlock();
    144   }
    145   uint8_t* storage = reinterpret_cast<uint8_t*>(
    146       AllocNonvirtualWithoutAccounting(bytes + sizeof(BlockHeader)));
    147   if (LIKELY(storage != nullptr)) {
    148     BlockHeader* header = reinterpret_cast<BlockHeader*>(storage);
    149     header->size_ = bytes;  // Write out the block header.
    150     storage += sizeof(BlockHeader);
    151     ++num_blocks_;
    152   }
    153   return storage;
    154 }
    155 
    156 void BumpPointerSpace::Walk(ObjectCallback* callback, void* arg) {
    157   uint8_t* pos = Begin();
    158   uint8_t* end = End();
    159   uint8_t* main_end = pos;
    160   {
    161     MutexLock mu(Thread::Current(), block_lock_);
    162     // If we have 0 blocks then we need to update the main header since we have bump pointer style
    163     // allocation into an unbounded region (actually bounded by Capacity()).
    164     if (num_blocks_ == 0) {
    165       UpdateMainBlock();
    166     }
    167     main_end = Begin() + main_block_size_;
    168     if (num_blocks_ == 0) {
    169       // We don't have any other blocks, this means someone else may be allocating into the main
    170       // block. In this case, we don't want to try and visit the other blocks after the main block
    171       // since these could actually be part of the main block.
    172       end = main_end;
    173     }
    174   }
    175   // Walk all of the objects in the main block first.
    176   while (pos < main_end) {
    177     mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
    178     // No read barrier because obj may not be a valid object.
    179     if (obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() == nullptr) {
    180       // There is a race condition where a thread has just allocated an object but not set the
    181       // class. We can't know the size of this object, so we don't visit it and exit the function
    182       // since there is guaranteed to be not other blocks.
    183       return;
    184     } else {
    185       callback(obj, arg);
    186       pos = reinterpret_cast<uint8_t*>(GetNextObject(obj));
    187     }
    188   }
    189   // Walk the other blocks (currently only TLABs).
    190   while (pos < end) {
    191     BlockHeader* header = reinterpret_cast<BlockHeader*>(pos);
    192     size_t block_size = header->size_;
    193     pos += sizeof(BlockHeader);  // Skip the header so that we know where the objects
    194     mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
    195     const mirror::Object* end_obj = reinterpret_cast<const mirror::Object*>(pos + block_size);
    196     CHECK_LE(reinterpret_cast<const uint8_t*>(end_obj), End());
    197     // We don't know how many objects are allocated in the current block. When we hit a null class
    198     // assume its the end. TODO: Have a thread update the header when it flushes the block?
    199     // No read barrier because obj may not be a valid object.
    200     while (obj < end_obj && obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
    201       callback(obj, arg);
    202       obj = GetNextObject(obj);
    203     }
    204     pos += block_size;
    205   }
    206 }
    207 
    208 accounting::ContinuousSpaceBitmap::SweepCallback* BumpPointerSpace::GetSweepCallback() {
    209   UNIMPLEMENTED(FATAL);
    210   UNREACHABLE();
    211 }
    212 
    213 uint64_t BumpPointerSpace::GetBytesAllocated() {
    214   // Start out pre-determined amount (blocks which are not being allocated into).
    215   uint64_t total = static_cast<uint64_t>(bytes_allocated_.LoadRelaxed());
    216   Thread* self = Thread::Current();
    217   MutexLock mu(self, *Locks::runtime_shutdown_lock_);
    218   MutexLock mu2(self, *Locks::thread_list_lock_);
    219   std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
    220   MutexLock mu3(Thread::Current(), block_lock_);
    221   // If we don't have any blocks, we don't have any thread local buffers. This check is required
    222   // since there can exist multiple bump pointer spaces which exist at the same time.
    223   if (num_blocks_ > 0) {
    224     for (Thread* thread : thread_list) {
    225       total += thread->GetThreadLocalBytesAllocated();
    226     }
    227   }
    228   return total;
    229 }
    230 
    231 uint64_t BumpPointerSpace::GetObjectsAllocated() {
    232   // Start out pre-determined amount (blocks which are not being allocated into).
    233   uint64_t total = static_cast<uint64_t>(objects_allocated_.LoadRelaxed());
    234   Thread* self = Thread::Current();
    235   MutexLock mu(self, *Locks::runtime_shutdown_lock_);
    236   MutexLock mu2(self, *Locks::thread_list_lock_);
    237   std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
    238   MutexLock mu3(Thread::Current(), block_lock_);
    239   // If we don't have any blocks, we don't have any thread local buffers. This check is required
    240   // since there can exist multiple bump pointer spaces which exist at the same time.
    241   if (num_blocks_ > 0) {
    242     for (Thread* thread : thread_list) {
    243       total += thread->GetThreadLocalObjectsAllocated();
    244     }
    245   }
    246   return total;
    247 }
    248 
    249 void BumpPointerSpace::RevokeThreadLocalBuffersLocked(Thread* thread) {
    250   objects_allocated_.FetchAndAddSequentiallyConsistent(thread->GetThreadLocalObjectsAllocated());
    251   bytes_allocated_.FetchAndAddSequentiallyConsistent(thread->GetThreadLocalBytesAllocated());
    252   thread->SetTlab(nullptr, nullptr);
    253 }
    254 
    255 bool BumpPointerSpace::AllocNewTlab(Thread* self, size_t bytes) {
    256   MutexLock mu(Thread::Current(), block_lock_);
    257   RevokeThreadLocalBuffersLocked(self);
    258   uint8_t* start = AllocBlock(bytes);
    259   if (start == nullptr) {
    260     return false;
    261   }
    262   self->SetTlab(start, start + bytes);
    263   return true;
    264 }
    265 
    266 void BumpPointerSpace::LogFragmentationAllocFailure(std::ostream& os,
    267                                                     size_t /* failed_alloc_bytes */) {
    268   size_t max_contiguous_allocation = Limit() - End();
    269   os << "; failed due to fragmentation (largest possible contiguous allocation "
    270      <<  max_contiguous_allocation << " bytes)";
    271   // Caller's job to print failed_alloc_bytes.
    272 }
    273 
    274 }  // namespace space
    275 }  // namespace gc
    276 }  // namespace art
    277