xref: /aosp_15_r20/art/libartbase/base/malloc_arena_pool.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2018 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include "malloc_arena_pool.h"
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker 
20*795d594fSAndroid Build Coastguard Worker #include <algorithm>
21*795d594fSAndroid Build Coastguard Worker #include <cstddef>
22*795d594fSAndroid Build Coastguard Worker #include <iomanip>
23*795d594fSAndroid Build Coastguard Worker #include <numeric>
24*795d594fSAndroid Build Coastguard Worker 
25*795d594fSAndroid Build Coastguard Worker #include <android-base/logging.h>
26*795d594fSAndroid Build Coastguard Worker #include "arena_allocator-inl.h"
27*795d594fSAndroid Build Coastguard Worker #include "mman.h"
28*795d594fSAndroid Build Coastguard Worker 
29*795d594fSAndroid Build Coastguard Worker namespace art {
30*795d594fSAndroid Build Coastguard Worker 
31*795d594fSAndroid Build Coastguard Worker class MallocArena final : public Arena {
32*795d594fSAndroid Build Coastguard Worker  public:
33*795d594fSAndroid Build Coastguard Worker   explicit MallocArena(size_t size = arena_allocator::kArenaDefaultSize);
34*795d594fSAndroid Build Coastguard Worker   virtual ~MallocArena();
35*795d594fSAndroid Build Coastguard Worker  private:
RequiredOverallocation()36*795d594fSAndroid Build Coastguard Worker   static constexpr size_t RequiredOverallocation() {
37*795d594fSAndroid Build Coastguard Worker     return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment)
38*795d594fSAndroid Build Coastguard Worker         ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t)
39*795d594fSAndroid Build Coastguard Worker         : 0u;
40*795d594fSAndroid Build Coastguard Worker   }
41*795d594fSAndroid Build Coastguard Worker 
42*795d594fSAndroid Build Coastguard Worker   uint8_t* unaligned_memory_;
43*795d594fSAndroid Build Coastguard Worker };
44*795d594fSAndroid Build Coastguard Worker 
MallocArena(size_t size)45*795d594fSAndroid Build Coastguard Worker MallocArena::MallocArena(size_t size) {
46*795d594fSAndroid Build Coastguard Worker   // We need to guarantee kArenaAlignment aligned allocation for the new arena.
47*795d594fSAndroid Build Coastguard Worker   // TODO: Use std::aligned_alloc() when it becomes available with C++17.
48*795d594fSAndroid Build Coastguard Worker   constexpr size_t overallocation = RequiredOverallocation();
49*795d594fSAndroid Build Coastguard Worker   unaligned_memory_ = reinterpret_cast<uint8_t*>(calloc(1, size + overallocation));
50*795d594fSAndroid Build Coastguard Worker   CHECK(unaligned_memory_ != nullptr);  // Abort on OOM.
51*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t));
52*795d594fSAndroid Build Coastguard Worker   if (overallocation == 0u) {
53*795d594fSAndroid Build Coastguard Worker     memory_ = unaligned_memory_;
54*795d594fSAndroid Build Coastguard Worker   } else {
55*795d594fSAndroid Build Coastguard Worker     memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment);
56*795d594fSAndroid Build Coastguard Worker     if (kRunningOnMemoryTool) {
57*795d594fSAndroid Build Coastguard Worker       size_t head = memory_ - unaligned_memory_;
58*795d594fSAndroid Build Coastguard Worker       size_t tail = overallocation - head;
59*795d594fSAndroid Build Coastguard Worker       MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head);
60*795d594fSAndroid Build Coastguard Worker       MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail);
61*795d594fSAndroid Build Coastguard Worker     }
62*795d594fSAndroid Build Coastguard Worker   }
63*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
64*795d594fSAndroid Build Coastguard Worker   size_ = size;
65*795d594fSAndroid Build Coastguard Worker }
66*795d594fSAndroid Build Coastguard Worker 
~MallocArena()67*795d594fSAndroid Build Coastguard Worker MallocArena::~MallocArena() {
68*795d594fSAndroid Build Coastguard Worker   constexpr size_t overallocation = RequiredOverallocation();
69*795d594fSAndroid Build Coastguard Worker   if (overallocation != 0u && kRunningOnMemoryTool) {
70*795d594fSAndroid Build Coastguard Worker     size_t head = memory_ - unaligned_memory_;
71*795d594fSAndroid Build Coastguard Worker     size_t tail = overallocation - head;
72*795d594fSAndroid Build Coastguard Worker     MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head);
73*795d594fSAndroid Build Coastguard Worker     MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail);
74*795d594fSAndroid Build Coastguard Worker   }
75*795d594fSAndroid Build Coastguard Worker   free(reinterpret_cast<void*>(unaligned_memory_));
76*795d594fSAndroid Build Coastguard Worker }
77*795d594fSAndroid Build Coastguard Worker 
Reset()78*795d594fSAndroid Build Coastguard Worker void Arena::Reset() {
79*795d594fSAndroid Build Coastguard Worker   if (bytes_allocated_ > 0) {
80*795d594fSAndroid Build Coastguard Worker     memset(Begin(), 0, bytes_allocated_);
81*795d594fSAndroid Build Coastguard Worker     bytes_allocated_ = 0;
82*795d594fSAndroid Build Coastguard Worker   }
83*795d594fSAndroid Build Coastguard Worker }
84*795d594fSAndroid Build Coastguard Worker 
MallocArenaPool()85*795d594fSAndroid Build Coastguard Worker MallocArenaPool::MallocArenaPool() : free_arenas_(nullptr) {
86*795d594fSAndroid Build Coastguard Worker }
87*795d594fSAndroid Build Coastguard Worker 
~MallocArenaPool()88*795d594fSAndroid Build Coastguard Worker MallocArenaPool::~MallocArenaPool() {
89*795d594fSAndroid Build Coastguard Worker   ReclaimMemory();
90*795d594fSAndroid Build Coastguard Worker }
91*795d594fSAndroid Build Coastguard Worker 
ReclaimMemory()92*795d594fSAndroid Build Coastguard Worker void MallocArenaPool::ReclaimMemory() {
93*795d594fSAndroid Build Coastguard Worker   while (free_arenas_ != nullptr) {
94*795d594fSAndroid Build Coastguard Worker     Arena* arena = free_arenas_;
95*795d594fSAndroid Build Coastguard Worker     free_arenas_ = free_arenas_->next_;
96*795d594fSAndroid Build Coastguard Worker     delete arena;
97*795d594fSAndroid Build Coastguard Worker   }
98*795d594fSAndroid Build Coastguard Worker }
99*795d594fSAndroid Build Coastguard Worker 
LockReclaimMemory()100*795d594fSAndroid Build Coastguard Worker void MallocArenaPool::LockReclaimMemory() {
101*795d594fSAndroid Build Coastguard Worker   std::lock_guard<std::mutex> lock(lock_);
102*795d594fSAndroid Build Coastguard Worker   ReclaimMemory();
103*795d594fSAndroid Build Coastguard Worker }
104*795d594fSAndroid Build Coastguard Worker 
AllocArena(size_t size)105*795d594fSAndroid Build Coastguard Worker Arena* MallocArenaPool::AllocArena(size_t size) {
106*795d594fSAndroid Build Coastguard Worker   Arena* ret = nullptr;
107*795d594fSAndroid Build Coastguard Worker   {
108*795d594fSAndroid Build Coastguard Worker     std::lock_guard<std::mutex> lock(lock_);
109*795d594fSAndroid Build Coastguard Worker     // We used to check only the first free arena but we're now checking two.
110*795d594fSAndroid Build Coastguard Worker     //
111*795d594fSAndroid Build Coastguard Worker     // FIXME: This is a workaround for `oatdump` running out of memory because of an allocation
112*795d594fSAndroid Build Coastguard Worker     // pattern where we would allocate a large arena (more than the default size) and then a
113*795d594fSAndroid Build Coastguard Worker     // normal one (default size) and then return them to the pool together, with the normal one
114*795d594fSAndroid Build Coastguard Worker     // passed as `first` to `FreeArenaChain()`, thus becoming the first in the `free_arenas_`
115*795d594fSAndroid Build Coastguard Worker     // list. Since we checked only the first arena, doing this repeatedly would never reuse the
116*795d594fSAndroid Build Coastguard Worker     // existing freed larger arenas and they would just accumulate in the free arena list until
117*795d594fSAndroid Build Coastguard Worker     // running out of memory. This workaround allows reusing the second arena in the list, thus
118*795d594fSAndroid Build Coastguard Worker     // fixing the problem for this specific allocation pattern. Similar allocation patterns
119*795d594fSAndroid Build Coastguard Worker     // with three or more arenas can still result in out of memory issues.
120*795d594fSAndroid Build Coastguard Worker     if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) {
121*795d594fSAndroid Build Coastguard Worker       ret = free_arenas_;
122*795d594fSAndroid Build Coastguard Worker       free_arenas_ = free_arenas_->next_;
123*795d594fSAndroid Build Coastguard Worker     } else if (free_arenas_ != nullptr &&
124*795d594fSAndroid Build Coastguard Worker                free_arenas_->next_ != nullptr &&
125*795d594fSAndroid Build Coastguard Worker                free_arenas_->next_->Size() >= size) {
126*795d594fSAndroid Build Coastguard Worker       ret = free_arenas_->next_;
127*795d594fSAndroid Build Coastguard Worker       free_arenas_->next_ = free_arenas_->next_->next_;
128*795d594fSAndroid Build Coastguard Worker     }
129*795d594fSAndroid Build Coastguard Worker   }
130*795d594fSAndroid Build Coastguard Worker   if (ret == nullptr) {
131*795d594fSAndroid Build Coastguard Worker     ret = new MallocArena(size);
132*795d594fSAndroid Build Coastguard Worker   }
133*795d594fSAndroid Build Coastguard Worker   ret->Reset();
134*795d594fSAndroid Build Coastguard Worker   return ret;
135*795d594fSAndroid Build Coastguard Worker }
136*795d594fSAndroid Build Coastguard Worker 
TrimMaps()137*795d594fSAndroid Build Coastguard Worker void MallocArenaPool::TrimMaps() {
138*795d594fSAndroid Build Coastguard Worker   // Nop, because there is no way to do madvise here.
139*795d594fSAndroid Build Coastguard Worker }
140*795d594fSAndroid Build Coastguard Worker 
GetBytesAllocated() const141*795d594fSAndroid Build Coastguard Worker size_t MallocArenaPool::GetBytesAllocated() const {
142*795d594fSAndroid Build Coastguard Worker   size_t total = 0;
143*795d594fSAndroid Build Coastguard Worker   std::lock_guard<std::mutex> lock(lock_);
144*795d594fSAndroid Build Coastguard Worker   for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
145*795d594fSAndroid Build Coastguard Worker     total += arena->GetBytesAllocated();
146*795d594fSAndroid Build Coastguard Worker   }
147*795d594fSAndroid Build Coastguard Worker   return total;
148*795d594fSAndroid Build Coastguard Worker }
149*795d594fSAndroid Build Coastguard Worker 
FreeArenaChain(Arena * first)150*795d594fSAndroid Build Coastguard Worker void MallocArenaPool::FreeArenaChain(Arena* first) {
151*795d594fSAndroid Build Coastguard Worker   if (kRunningOnMemoryTool) {
152*795d594fSAndroid Build Coastguard Worker     for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
153*795d594fSAndroid Build Coastguard Worker       MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
154*795d594fSAndroid Build Coastguard Worker     }
155*795d594fSAndroid Build Coastguard Worker   }
156*795d594fSAndroid Build Coastguard Worker 
157*795d594fSAndroid Build Coastguard Worker   if (arena_allocator::kArenaAllocatorPreciseTracking) {
158*795d594fSAndroid Build Coastguard Worker     // Do not reuse arenas when tracking.
159*795d594fSAndroid Build Coastguard Worker     while (first != nullptr) {
160*795d594fSAndroid Build Coastguard Worker       Arena* next = first->next_;
161*795d594fSAndroid Build Coastguard Worker       delete first;
162*795d594fSAndroid Build Coastguard Worker       first = next;
163*795d594fSAndroid Build Coastguard Worker     }
164*795d594fSAndroid Build Coastguard Worker     return;
165*795d594fSAndroid Build Coastguard Worker   }
166*795d594fSAndroid Build Coastguard Worker 
167*795d594fSAndroid Build Coastguard Worker   if (first != nullptr) {
168*795d594fSAndroid Build Coastguard Worker     Arena* last = first;
169*795d594fSAndroid Build Coastguard Worker     while (last->next_ != nullptr) {
170*795d594fSAndroid Build Coastguard Worker       last = last->next_;
171*795d594fSAndroid Build Coastguard Worker     }
172*795d594fSAndroid Build Coastguard Worker     std::lock_guard<std::mutex> lock(lock_);
173*795d594fSAndroid Build Coastguard Worker     last->next_ = free_arenas_;
174*795d594fSAndroid Build Coastguard Worker     free_arenas_ = first;
175*795d594fSAndroid Build Coastguard Worker   }
176*795d594fSAndroid Build Coastguard Worker }
177*795d594fSAndroid Build Coastguard Worker 
178*795d594fSAndroid Build Coastguard Worker }  // namespace art
179