arena_allocator.cc revision f44d36c8423f81cbb5e9f55d8813e26ffa1a7f3b
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 <algorithm> 18#include <cstddef> 19#include <iomanip> 20#include <numeric> 21 22#include "arena_allocator.h" 23#include "logging.h" 24#include "mem_map.h" 25#include "mutex.h" 26#include "thread-inl.h" 27#include "systrace.h" 28 29namespace art { 30 31constexpr size_t kMemoryToolRedZoneBytes = 8; 32constexpr size_t Arena::kDefaultSize; 33 34template <bool kCount> 35const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = { 36 "Misc ", 37 "SwitchTbl ", 38 "SlowPaths ", 39 "GrowBitMap ", 40 "STL ", 41 "GraphBuilder ", 42 "Graph ", 43 "BasicBlock ", 44 "BlockList ", 45 "RevPostOrder ", 46 "LinearOrder ", 47 "ConstantsMap ", 48 "Predecessors ", 49 "Successors ", 50 "Dominated ", 51 "Instruction ", 52 "InvokeInputs ", 53 "PhiInputs ", 54 "LoopInfo ", 55 "LIBackEdges ", 56 "TryCatchInf ", 57 "UseListNode ", 58 "Environment ", 59 "EnvVRegs ", 60 "EnvLocations ", 61 "LocSummary ", 62 "SsaBuilder ", 63 "MoveOperands ", 64 "CodeBuffer ", 65 "StackMaps ", 66 "Optimization ", 67 "GVN ", 68 "InductionVar ", 69 "BCE ", 70 "DCE ", 71 "LSE ", 72 "LICM ", 73 "LoopOpt ", 74 "SsaLiveness ", 75 "SsaPhiElim ", 76 "RefTypeProp ", 77 "SideEffects ", 78 "RegAllocator ", 79 "RegAllocVldt ", 80 "StackMapStm ", 81 "CodeGen ", 82 "Assembler ", 83 "ParallelMove ", 84 "GraphChecker ", 85 "Verifier ", 86 "CallingConv ", 87 "CHA ", 88 "Scheduler ", 89}; 90 91template <bool kCount> 92ArenaAllocatorStatsImpl<kCount>::ArenaAllocatorStatsImpl() 93 : num_allocations_(0u), 94 alloc_stats_(kNumArenaAllocKinds, 0u) { 95} 96 97template <bool kCount> 98void ArenaAllocatorStatsImpl<kCount>::Copy(const ArenaAllocatorStatsImpl& other) { 99 num_allocations_ = other.num_allocations_; 100 std::copy_n(other.alloc_stats_.begin(), kNumArenaAllocKinds, alloc_stats_.begin()); 101} 102 103template <bool kCount> 104void ArenaAllocatorStatsImpl<kCount>::RecordAlloc(size_t bytes, ArenaAllocKind kind) { 105 alloc_stats_[kind] += bytes; 106 ++num_allocations_; 107} 108 109template <bool kCount> 110size_t ArenaAllocatorStatsImpl<kCount>::NumAllocations() const { 111 return num_allocations_; 112} 113 114template <bool kCount> 115size_t ArenaAllocatorStatsImpl<kCount>::BytesAllocated() const { 116 const size_t init = 0u; // Initial value of the correct type. 117 return std::accumulate(alloc_stats_.begin(), alloc_stats_.end(), init); 118} 119 120template <bool kCount> 121void ArenaAllocatorStatsImpl<kCount>::Dump(std::ostream& os, const Arena* first, 122 ssize_t lost_bytes_adjustment) const { 123 size_t malloc_bytes = 0u; 124 size_t lost_bytes = 0u; 125 size_t num_arenas = 0u; 126 for (const Arena* arena = first; arena != nullptr; arena = arena->next_) { 127 malloc_bytes += arena->Size(); 128 lost_bytes += arena->RemainingSpace(); 129 ++num_arenas; 130 } 131 // The lost_bytes_adjustment is used to make up for the fact that the current arena 132 // may not have the bytes_allocated_ updated correctly. 133 lost_bytes += lost_bytes_adjustment; 134 const size_t bytes_allocated = BytesAllocated(); 135 os << " MEM: used: " << bytes_allocated << ", allocated: " << malloc_bytes 136 << ", lost: " << lost_bytes << "\n"; 137 size_t num_allocations = NumAllocations(); 138 if (num_allocations != 0) { 139 os << "Number of arenas allocated: " << num_arenas << ", Number of allocations: " 140 << num_allocations << ", avg size: " << bytes_allocated / num_allocations << "\n"; 141 } 142 os << "===== Allocation by kind\n"; 143 static_assert(arraysize(kAllocNames) == kNumArenaAllocKinds, "arraysize of kAllocNames"); 144 for (int i = 0; i < kNumArenaAllocKinds; i++) { 145 os << kAllocNames[i] << std::setw(10) << alloc_stats_[i] << "\n"; 146 } 147} 148 149#pragma GCC diagnostic push 150#if __clang_major__ >= 4 151#pragma GCC diagnostic ignored "-Winstantiation-after-specialization" 152#endif 153// Explicitly instantiate the used implementation. 154template class ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations>; 155#pragma GCC diagnostic pop 156 157void ArenaAllocatorMemoryTool::DoMakeDefined(void* ptr, size_t size) { 158 MEMORY_TOOL_MAKE_DEFINED(ptr, size); 159} 160 161void ArenaAllocatorMemoryTool::DoMakeUndefined(void* ptr, size_t size) { 162 MEMORY_TOOL_MAKE_UNDEFINED(ptr, size); 163} 164 165void ArenaAllocatorMemoryTool::DoMakeInaccessible(void* ptr, size_t size) { 166 MEMORY_TOOL_MAKE_NOACCESS(ptr, size); 167} 168 169Arena::Arena() : bytes_allocated_(0), next_(nullptr) { 170} 171 172class MallocArena FINAL : public Arena { 173 public: 174 explicit MallocArena(size_t size = Arena::kDefaultSize); 175 virtual ~MallocArena(); 176 private: 177 static constexpr size_t RequiredOverallocation() { 178 return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment) 179 ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t) 180 : 0u; 181 } 182 183 uint8_t* unaligned_memory_; 184}; 185 186MallocArena::MallocArena(size_t size) { 187 // We need to guarantee kArenaAlignment aligned allocation for the new arena. 188 // TODO: Use std::aligned_alloc() when it becomes available with C++17. 189 constexpr size_t overallocation = RequiredOverallocation(); 190 unaligned_memory_ = reinterpret_cast<uint8_t*>(calloc(1, size + overallocation)); 191 CHECK(unaligned_memory_ != nullptr); // Abort on OOM. 192 DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t)); 193 if (overallocation == 0u) { 194 memory_ = unaligned_memory_; 195 } else { 196 memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment); 197 if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { 198 size_t head = memory_ - unaligned_memory_; 199 size_t tail = overallocation - head; 200 MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head); 201 MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail); 202 } 203 } 204 DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment); 205 size_ = size; 206} 207 208MallocArena::~MallocArena() { 209 constexpr size_t overallocation = RequiredOverallocation(); 210 if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { 211 size_t head = memory_ - unaligned_memory_; 212 size_t tail = overallocation - head; 213 MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head); 214 MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail); 215 } 216 free(reinterpret_cast<void*>(unaligned_memory_)); 217} 218 219class MemMapArena FINAL : public Arena { 220 public: 221 MemMapArena(size_t size, bool low_4gb, const char* name); 222 virtual ~MemMapArena(); 223 void Release() OVERRIDE; 224 225 private: 226 std::unique_ptr<MemMap> map_; 227}; 228 229MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) { 230 // Round up to a full page as that's the smallest unit of allocation for mmap() 231 // and we want to be able to use all memory that we actually allocate. 232 size = RoundUp(size, kPageSize); 233 std::string error_msg; 234 map_.reset(MemMap::MapAnonymous( 235 name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg)); 236 CHECK(map_.get() != nullptr) << error_msg; 237 memory_ = map_->Begin(); 238 static_assert(ArenaAllocator::kArenaAlignment <= kPageSize, 239 "Arena should not need stronger alignment than kPageSize."); 240 DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment); 241 size_ = map_->Size(); 242} 243 244MemMapArena::~MemMapArena() { 245 // Destroys MemMap via std::unique_ptr<>. 246} 247 248void MemMapArena::Release() { 249 if (bytes_allocated_ > 0) { 250 map_->MadviseDontNeedAndZero(); 251 bytes_allocated_ = 0; 252 } 253} 254 255void Arena::Reset() { 256 if (bytes_allocated_ > 0) { 257 memset(Begin(), 0, bytes_allocated_); 258 bytes_allocated_ = 0; 259 } 260} 261 262ArenaPool::ArenaPool(bool use_malloc, bool low_4gb, const char* name) 263 : use_malloc_(use_malloc), 264 lock_("Arena pool lock", kArenaPoolLock), 265 free_arenas_(nullptr), 266 low_4gb_(low_4gb), 267 name_(name) { 268 if (low_4gb) { 269 CHECK(!use_malloc) << "low4gb must use map implementation"; 270 } 271 if (!use_malloc) { 272 MemMap::Init(); 273 } 274} 275 276ArenaPool::~ArenaPool() { 277 ReclaimMemory(); 278} 279 280void ArenaPool::ReclaimMemory() { 281 while (free_arenas_ != nullptr) { 282 auto* arena = free_arenas_; 283 free_arenas_ = free_arenas_->next_; 284 delete arena; 285 } 286} 287 288void ArenaPool::LockReclaimMemory() { 289 MutexLock lock(Thread::Current(), lock_); 290 ReclaimMemory(); 291} 292 293Arena* ArenaPool::AllocArena(size_t size) { 294 Thread* self = Thread::Current(); 295 Arena* ret = nullptr; 296 { 297 MutexLock lock(self, lock_); 298 if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) { 299 ret = free_arenas_; 300 free_arenas_ = free_arenas_->next_; 301 } 302 } 303 if (ret == nullptr) { 304 ret = use_malloc_ ? static_cast<Arena*>(new MallocArena(size)) : 305 new MemMapArena(size, low_4gb_, name_); 306 } 307 ret->Reset(); 308 return ret; 309} 310 311void ArenaPool::TrimMaps() { 312 if (!use_malloc_) { 313 ScopedTrace trace(__PRETTY_FUNCTION__); 314 // Doesn't work for malloc. 315 MutexLock lock(Thread::Current(), lock_); 316 for (auto* arena = free_arenas_; arena != nullptr; arena = arena->next_) { 317 arena->Release(); 318 } 319 } 320} 321 322size_t ArenaPool::GetBytesAllocated() const { 323 size_t total = 0; 324 MutexLock lock(Thread::Current(), lock_); 325 for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) { 326 total += arena->GetBytesAllocated(); 327 } 328 return total; 329} 330 331void ArenaPool::FreeArenaChain(Arena* first) { 332 if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) { 333 for (Arena* arena = first; arena != nullptr; arena = arena->next_) { 334 MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_); 335 } 336 } 337 if (first != nullptr) { 338 Arena* last = first; 339 while (last->next_ != nullptr) { 340 last = last->next_; 341 } 342 Thread* self = Thread::Current(); 343 MutexLock lock(self, lock_); 344 last->next_ = free_arenas_; 345 free_arenas_ = first; 346 } 347} 348 349size_t ArenaAllocator::BytesAllocated() const { 350 return ArenaAllocatorStats::BytesAllocated(); 351} 352 353size_t ArenaAllocator::BytesUsed() const { 354 size_t total = ptr_ - begin_; 355 if (arena_head_ != nullptr) { 356 for (Arena* cur_arena = arena_head_->next_; cur_arena != nullptr; 357 cur_arena = cur_arena->next_) { 358 total += cur_arena->GetBytesAllocated(); 359 } 360 } 361 return total; 362} 363 364ArenaAllocator::ArenaAllocator(ArenaPool* pool) 365 : pool_(pool), 366 begin_(nullptr), 367 end_(nullptr), 368 ptr_(nullptr), 369 arena_head_(nullptr) { 370} 371 372void ArenaAllocator::UpdateBytesAllocated() { 373 if (arena_head_ != nullptr) { 374 // Update how many bytes we have allocated into the arena so that the arena pool knows how 375 // much memory to zero out. 376 arena_head_->bytes_allocated_ = ptr_ - begin_; 377 } 378} 379 380void* ArenaAllocator::AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind) { 381 // We mark all memory for a newly retrieved arena as inaccessible and then 382 // mark only the actually allocated memory as defined. That leaves red zones 383 // and padding between allocations marked as inaccessible. 384 size_t rounded_bytes = RoundUp(bytes + kMemoryToolRedZoneBytes, 8); 385 ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind); 386 uint8_t* ret; 387 if (UNLIKELY(rounded_bytes > static_cast<size_t>(end_ - ptr_))) { 388 ret = AllocFromNewArenaWithMemoryTool(rounded_bytes); 389 } else { 390 ret = ptr_; 391 ptr_ += rounded_bytes; 392 } 393 MEMORY_TOOL_MAKE_DEFINED(ret, bytes); 394 // Check that the memory is already zeroed out. 395 DCHECK(std::all_of(ret, ret + bytes, [](uint8_t val) { return val == 0u; })); 396 return ret; 397} 398 399void* ArenaAllocator::AllocWithMemoryToolAlign16(size_t bytes, ArenaAllocKind kind) { 400 // We mark all memory for a newly retrieved arena as inaccessible and then 401 // mark only the actually allocated memory as defined. That leaves red zones 402 // and padding between allocations marked as inaccessible. 403 size_t rounded_bytes = bytes + kMemoryToolRedZoneBytes; 404 DCHECK_ALIGNED(rounded_bytes, 8); // `bytes` is 16-byte aligned, red zone is 8-byte aligned. 405 uintptr_t padding = 406 ((reinterpret_cast<uintptr_t>(ptr_) + 15u) & 15u) - reinterpret_cast<uintptr_t>(ptr_); 407 ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind); 408 uint8_t* ret; 409 if (UNLIKELY(padding + rounded_bytes > static_cast<size_t>(end_ - ptr_))) { 410 static_assert(kArenaAlignment >= 16, "Expecting sufficient alignment for new Arena."); 411 ret = AllocFromNewArenaWithMemoryTool(rounded_bytes); 412 } else { 413 ptr_ += padding; // Leave padding inaccessible. 414 ret = ptr_; 415 ptr_ += rounded_bytes; 416 } 417 MEMORY_TOOL_MAKE_DEFINED(ret, bytes); 418 // Check that the memory is already zeroed out. 419 DCHECK(std::all_of(ret, ret + bytes, [](uint8_t val) { return val == 0u; })); 420 return ret; 421} 422 423ArenaAllocator::~ArenaAllocator() { 424 // Reclaim all the arenas by giving them back to the thread pool. 425 UpdateBytesAllocated(); 426 pool_->FreeArenaChain(arena_head_); 427} 428 429uint8_t* ArenaAllocator::AllocFromNewArena(size_t bytes) { 430 Arena* new_arena = pool_->AllocArena(std::max(Arena::kDefaultSize, bytes)); 431 DCHECK(new_arena != nullptr); 432 DCHECK_LE(bytes, new_arena->Size()); 433 if (static_cast<size_t>(end_ - ptr_) > new_arena->Size() - bytes) { 434 // The old arena has more space remaining than the new one, so keep using it. 435 // This can happen when the requested size is over half of the default size. 436 DCHECK(arena_head_ != nullptr); 437 new_arena->bytes_allocated_ = bytes; // UpdateBytesAllocated() on the new_arena. 438 new_arena->next_ = arena_head_->next_; 439 arena_head_->next_ = new_arena; 440 } else { 441 UpdateBytesAllocated(); 442 new_arena->next_ = arena_head_; 443 arena_head_ = new_arena; 444 // Update our internal data structures. 445 begin_ = new_arena->Begin(); 446 DCHECK_ALIGNED(begin_, kAlignment); 447 ptr_ = begin_ + bytes; 448 end_ = new_arena->End(); 449 } 450 return new_arena->Begin(); 451} 452 453uint8_t* ArenaAllocator::AllocFromNewArenaWithMemoryTool(size_t bytes) { 454 uint8_t* ret = AllocFromNewArena(bytes); 455 uint8_t* noaccess_begin = ret + bytes; 456 uint8_t* noaccess_end; 457 if (ret == arena_head_->Begin()) { 458 DCHECK(ptr_ - bytes == ret); 459 noaccess_end = end_; 460 } else { 461 // We're still using the old arena but `ret` comes from a new one just after it. 462 DCHECK(arena_head_->next_ != nullptr); 463 DCHECK(ret == arena_head_->next_->Begin()); 464 DCHECK_EQ(bytes, arena_head_->next_->GetBytesAllocated()); 465 noaccess_end = arena_head_->next_->End(); 466 } 467 MEMORY_TOOL_MAKE_NOACCESS(noaccess_begin, noaccess_end - noaccess_begin); 468 return ret; 469} 470 471bool ArenaAllocator::Contains(const void* ptr) const { 472 if (ptr >= begin_ && ptr < end_) { 473 return true; 474 } 475 for (const Arena* cur_arena = arena_head_; cur_arena != nullptr; cur_arena = cur_arena->next_) { 476 if (cur_arena->Contains(ptr)) { 477 return true; 478 } 479 } 480 return false; 481} 482 483MemStats::MemStats(const char* name, 484 const ArenaAllocatorStats* stats, 485 const Arena* first_arena, 486 ssize_t lost_bytes_adjustment) 487 : name_(name), 488 stats_(stats), 489 first_arena_(first_arena), 490 lost_bytes_adjustment_(lost_bytes_adjustment) { 491} 492 493void MemStats::Dump(std::ostream& os) const { 494 os << name_ << " stats:\n"; 495 stats_->Dump(os, first_arena_, lost_bytes_adjustment_); 496} 497 498// Dump memory usage stats. 499MemStats ArenaAllocator::GetMemStats() const { 500 ssize_t lost_bytes_adjustment = 501 (arena_head_ == nullptr) ? 0 : (end_ - ptr_) - arena_head_->RemainingSpace(); 502 return MemStats("ArenaAllocator", this, arena_head_, lost_bytes_adjustment); 503} 504 505} // namespace art 506