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