1 /** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef PANDA_RUNTIME_MEM_HEAP_SPACE_H 17 #define PANDA_RUNTIME_MEM_HEAP_SPACE_H 18 19 #include "libpandabase/mem/mem_pool.h" 20 #include "libpandabase/os/mutex.h" 21 #include "libpandabase/macros.h" 22 23 #include <optional> 24 25 namespace panda::mem { 26 27 namespace test { 28 class HeapSpaceTest; 29 class MemStatsGenGCTest; 30 template <typename ObjectAllocator, bool RegularSpace> 31 class RegionAllocatorTestBase; 32 class RegionAllocatorTest; 33 class RegionNonmovableObjectAllocatorTest; 34 class RegionGarbageChoosingTest; 35 class RemSetTest; 36 } // namespace test 37 38 // Object allocation flow: 39 // --------+ 40 // +------+ +-----------+ +------------+ | 41 // | GC | <----+ | Runtime | ... | Compiler | | 42 // +------+ | +-----------+ | +------------+ | 43 // | | +---+ | | 44 // +-----+ | | +---------+ | Standard flow for object allocation 45 // | | | | | 46 // v v v v | 47 // +-------------------+ | 48 // | ObjectAllocator | | 49 // +-------------------+ --------+ 50 // ^ | 51 // | | (if need new pool) 52 // +--------------------+ | 53 // | v 54 // | if need trigger GC +-------------+ 55 // + <------------------| HeapSpace | <---- Here we compute heap limits after GC and make the allocation decision 56 // | NULLPOOL +-------------+ 57 // | | (if can alloc) 58 // | v 59 // | Pool +-------------+ 60 // +---------<----------| PoolManager | 61 // +-------------+ 62 /** 63 * \brief Class for description of object spaces and limits (minimum, maximum, current sizes). 64 * Pools for object spaces is allocated via this class. Also this class cooperate with PoolManager for info getting 65 * about object heap. 66 * Class is some virtual layer between PoolManager and Object allocators and GC for object pools allocations. 67 * HeapSpace is used for non-generational-based GC and as base class for GenerationalSpaces class 68 */ 69 class HeapSpace { 70 public: 71 explicit HeapSpace() = default; 72 NO_COPY_SEMANTIC(HeapSpace); 73 NO_MOVE_SEMANTIC(HeapSpace); 74 virtual ~HeapSpace() = default; 75 76 /** 77 * \brief Heap space inilialization 78 * @param initial_size initial (also minimum) heap space size 79 * @param max_size maximum heap space size 80 * @param min_free_percentage minimum possible percentage of free memory in this heap space, use for heap increasing 81 * computation 82 * @param max_free_percentage maximum possible percentage of free memory in this heap space, use for heap reducing 83 * computation 84 */ 85 void Initialize(size_t initial_size, size_t max_size, uint32_t min_free_percentage, uint32_t max_free_percentage); 86 87 /** 88 * \brief Compute new size of heap space 89 */ 90 virtual void ComputeNewSize(); 91 92 /** 93 * \brief Get current used heap size 94 */ 95 virtual size_t GetHeapSize() const; 96 97 /** 98 * \brief Try to allocate pool via PoolManager 99 */ 100 [[nodiscard]] virtual Pool TryAllocPool(size_t pool_size, SpaceType space_type, AllocatorType allocator_type, 101 void *allocator_ptr); 102 103 /** 104 * \brief Try to allocate arena via PoolManager 105 */ 106 [[nodiscard]] virtual Arena *TryAllocArena(size_t arena_size, SpaceType space_type, AllocatorType allocator_type, 107 void *allocator_ptr); 108 109 /** 110 * \brief Free pool via PoolManager 111 */ 112 void FreePool(void *pool_mem, size_t pool_size); 113 114 /** 115 * \brief Free arena via PoolManager 116 */ 117 void FreeArena(Arena *arena); 118 GetMinFreePercentage()119 double GetMinFreePercentage() const 120 { 121 return min_free_percentage_; 122 } 123 GetMaxFreePercentage()124 double GetMaxFreePercentage() const 125 { 126 return max_free_percentage_; 127 } 128 SetIsWorkGC(bool value)129 void SetIsWorkGC(bool value) 130 { 131 is_work_gc_ = value; 132 } 133 134 protected: 135 class ObjectMemorySpace { 136 public: 137 explicit ObjectMemorySpace() = default; 138 DEFAULT_COPY_SEMANTIC(ObjectMemorySpace); 139 DEFAULT_NOEXCEPT_MOVE_SEMANTIC(ObjectMemorySpace); 140 ~ObjectMemorySpace() = default; 141 142 void Initialize(size_t initial_size, size_t max_size); 143 GetCurrentSize()144 size_t GetCurrentSize() const 145 { 146 return current_size_; 147 } 148 GetMaxSize()149 size_t GetMaxSize() const 150 { 151 return max_size_; 152 } 153 GetCurrentNonOccupiedSize()154 size_t GetCurrentNonOccupiedSize() const 155 { 156 return max_size_ - current_size_; 157 } 158 UseFullSpace()159 void UseFullSpace() 160 { 161 current_size_ = max_size_; 162 } 163 164 /** 165 * \brief Compute new size of heap space 166 */ 167 void ComputeNewSize(size_t free_bytes, double min_free_percentage, double max_free_percentage); 168 169 /** 170 * \brief Increase current heap space 171 * @param bytes bytes count for heap space increasing 172 */ 173 void IncreaseBy(uint64_t bytes); 174 175 /** 176 * \brief Reduce current heap space 177 * @param bytes bytes count for heap space reducing 178 */ 179 void ReduceBy(size_t bytes); 180 181 // If we have too big pool for allocation then after space increasing we can have not memory for this pool, 182 // so we save pool size and increase heap space to allocate such pool 183 size_t saved_pool_size {0}; // NOLINT(misc-non-private-member-variables-in-classes) 184 185 private: 186 // min_size_ <= current_size_ <= max_size_ 187 size_t current_size_ {0}; 188 size_t min_size_ {0}; 189 size_t max_size_ {0}; 190 }; 191 192 // For avoid zero division 193 static constexpr uint32_t MAX_FREE_PERCENTAGE = 99; 194 195 void InitializePercentages(uint32_t min_free_percentage, uint32_t max_free_percentage); 196 197 [[nodiscard]] Pool TryAllocPoolBase(size_t pool_size, SpaceType space_type, AllocatorType allocator_type, 198 void *allocator_ptr, size_t current_free_bytes_in_space, 199 ObjectMemorySpace *mem_space); 200 201 [[nodiscard]] Arena *TryAllocArenaBase(size_t arena_size, SpaceType space_type, AllocatorType allocator_type, 202 void *allocator_ptr, size_t current_free_bytes_in_space, 203 ObjectMemorySpace *mem_space); 204 205 /** 206 * \brief Check that we can to allocate requested size into target space 207 * 208 * @param pool_size size of memory chunk for allocation 209 * @param current_free_bytes_in_space current freebytes count into target space 210 * @param mem_space target space in which there will be allocation 211 * 212 * @return std::optional value. If result has value then it contains bytes count for heap increasing (need during 213 * GC, for example), else need to trigger GC 214 */ 215 std::optional<size_t> WillAlloc(size_t pool_size, size_t current_free_bytes_in_space, 216 const ObjectMemorySpace *mem_space) const; 217 218 /** 219 * @return current maximum size of this heap space 220 */ 221 size_t GetCurrentSize() const; 222 223 /** 224 * @return free bytes count for current heap space size 225 */ 226 size_t GetCurrentFreeBytes(size_t bytes_not_in_this_space = 0) const; 227 IsWorkGC()228 inline bool IsWorkGC() const 229 { 230 return is_work_gc_; 231 } 232 233 mutable os::memory::RWLock heap_lock_; // NOLINT(misc-non-private-member-variables-in-classes) 234 ObjectMemorySpace mem_space_; // NOLINT(misc-non-private-member-variables-in-classes) 235 bool is_initialized_ {false}; // NOLINT(misc-non-private-member-variables-in-classes) 236 237 private: 238 // if GC wants allocate memory, but we have not needed memory for this then we increase current heap space 239 bool is_work_gc_ {false}; 240 double min_free_percentage_ {0}; 241 double max_free_percentage_ {0}; 242 243 friend class panda::mem::test::RegionGarbageChoosingTest; 244 template <typename ObjectAllocator, bool RegularSpace> 245 friend class panda::mem::test::RegionAllocatorTestBase; 246 friend class panda::mem::test::RegionAllocatorTest; 247 friend class panda::mem::test::RegionNonmovableObjectAllocatorTest; 248 friend class panda::mem::test::RemSetTest; 249 friend class panda::mem::test::HeapSpaceTest; 250 }; 251 252 // Y - memory chunk is used for objects in young space 253 // T - memory chunk is used for objects in tenured space 254 // F - memory chunk which is not used for any space 255 // 256 // +---------------------------------------------------------------------------------------------------------+ 257 // | Real object memory chunks location allocating via PoolManager | 258 // |---------------------------------------------------------------------------------------------------------| 259 // | shared pool (use for G1-GC) young, tenured and free pools | 260 // |-----------------------------|---------------------------------------------------------------------------| 261 // |~~~| | |~~~|~~~| |~~~~~| |~~~~~~~~~| |~~~~~~~~~~~~~| | 262 // |~~~| | |~~~|~~~| |~~~~~| F |~~~~~~~~~| F |~~~~~~~~~~~~~| | 263 // |~Y~| F | F |~Y~|~T~| F |~~Y~~| (free |~~~~T~~~~| (free |~~~~~~T~~~~~~| non-occupied memory (F) | 264 // |~~~| | |~~~|~~~| |~~~~~| pool |~~~~~~~~~| pool |~~~~~~~~~~~~~| | 265 // |~~~| | |~~~|~~~| |~~~~~| map) |~~~~~~~~~| map) |~~~~~~~~~~~~~| | 266 // +---------------------------------------------------------------------------------------------------------+ 267 // ^ | | | | | | ^ ^ 268 // | | | | | | +----------+ | | 269 // | | +-----------+ | | | | | | 270 // | | | +----------|----------+ +--------------+ | all occupied memory via PoolManager | 271 // | | | | +-------+ | +------------+ | 272 // | | | | | | | | 273 // v v v v v v v v 274 // +---------------------------|-----------------------------------------------------------------------------+ 275 // |~~~|~~~|~~~~~| | |~~~|~~~~~~~|~~~~~~~~~~~~~| | | 276 // |~~~|~~~|~~~~~| | |~~~|~~~~~~~|~~~~~~~~~~~~~| | | 277 // |~Y~|~Y~|~~Y~~| F | F |~T~|~~~T~~~|~~~~~~T~~~~~~| F | F | 278 // |~~~|~~~|~~~~~| | |~~~|~~~~~~~|~~~~~~~~~~~~~| | | 279 // |~~~|~~~|~~~~~| | |~~~|~~~~~~~|~~~~~~~~~~~~~| | | 280 // |-------------|-----|-------|-------------------------|-------------------------|-------------------------| 281 // | used | | | used | | | 282 // |-------------------|-------|---------------------------------------------------|-------------------------| 283 // | current max size | | current max size | | 284 // |---------------------------|-----------------------------------------------------------------------------| 285 // | Young space | Tenured space | 286 // |---------------------------|-----------------------------------------------------------------------------| 287 // | young-space-size object-memory-pool 288 // | argument argument 289 // |---------------------------------------------------------------------------------------------------------| 290 // | Generations memory representation | 291 // +---------------------------------------------------------------------------------------------------------+ 292 // 293 // init-young-space-size argument set minimum (initial) current max size for Young space 294 // init-object-memory-pool argument set minimum (initial) current max size for heap 295 /** 296 * Class for description of object spaces and limits (minimum, maximum, current sizes) for generatinal-based GC. 297 * Pools for young and tenured spaces is allocated via this class. Also this class cooperate with PoolManager for info 298 * getting about object spaces. If current max size is not enough for new pool in some space then need to trigger GC 299 */ 300 class GenerationalSpaces final : public HeapSpace { 301 public: 302 explicit GenerationalSpaces() = default; 303 NO_COPY_SEMANTIC(GenerationalSpaces); 304 NO_MOVE_SEMANTIC(GenerationalSpaces); 305 ~GenerationalSpaces() override = default; 306 307 void Initialize(size_t initial_young_size, bool was_set_initial_young_size, size_t max_young_size, 308 bool was_set_max_young_size, size_t initial_total_size, size_t max_total_size, 309 uint32_t min_free_percentage, uint32_t max_free_percentage); 310 311 /** 312 * \brief Compute new sizes for young and tenured spaces 313 */ 314 void ComputeNewSize() override; 315 316 size_t GetHeapSize() const override; 317 318 // Use for CanAllocInSpace 319 static constexpr bool IS_YOUNG_SPACE = true; 320 static constexpr bool IS_TENURED_SPACE = false; 321 322 /** 323 * @return true if we can allocate requested size in space 324 * 325 * @param is_young young or tenured space 326 * @param chunk_size memory size for allocating in space 327 */ 328 bool CanAllocInSpace(bool is_young, size_t chunk_size) const; 329 330 size_t GetCurrentMaxYoungSize() const; 331 332 size_t GetMaxYoungSize() const; 333 334 void UseFullYoungSpace(); 335 336 /** 337 * \brief Allocate alone young pool (use for Gen-GC) with maximum possible size of young space 338 */ 339 [[nodiscard]] Pool AllocAlonePoolForYoung(SpaceType space_type, AllocatorType allocator_type, void *allocator_ptr); 340 341 /** 342 * \brief Try allocate pool for young space (use for G1-GC). 343 * If free size in young space less requested pool size then pool can be allocate only during GC work 344 */ 345 [[nodiscard]] Pool TryAllocPoolForYoung(size_t pool_size, SpaceType space_type, AllocatorType allocator_type, 346 void *allocator_ptr); 347 348 /** 349 * \brief Try allocate pool for tenured space (use for generational-based GC). 350 * If free size in tenured space less requested pool size then pool can be allocate only during GC work 351 */ 352 [[nodiscard]] Pool TryAllocPoolForTenured(size_t pool_size, SpaceType space_type, AllocatorType allocator_type, 353 void *allocator_ptr); 354 355 [[nodiscard]] Pool TryAllocPool(size_t pool_size, SpaceType space_type, AllocatorType allocator_type, 356 void *allocator_ptr) final; 357 358 [[nodiscard]] Arena *TryAllocArenaForTenured(size_t arena_size, SpaceType space_type, AllocatorType allocator_type, 359 void *allocator_ptr); 360 361 [[nodiscard]] Arena *TryAllocArena(size_t arena_size, SpaceType space_type, AllocatorType allocator_type, 362 void *allocator_ptr) final; 363 364 /** 365 * \brief Allocate pool shared usage between young and tenured spaces (use for regions in G1-GC). 366 */ 367 [[nodiscard]] Pool AllocSharedPool(size_t pool_size, SpaceType space_type, AllocatorType allocator_type, 368 void *allocator_ptr); 369 370 void IncreaseYoungOccupiedInSharedPool(size_t chunk_size); 371 372 void IncreaseTenuredOccupiedInSharedPool(size_t chunk_size); 373 374 void ReduceYoungOccupiedInSharedPool(size_t chunk_size); 375 376 void ReduceTenuredOccupiedInSharedPool(size_t chunk_size); 377 378 void FreeSharedPool(void *pool_mem, size_t pool_size); 379 380 void FreeYoungPool(void *pool_mem, size_t pool_size); 381 382 void PromoteYoungPool(size_t pool_size); 383 384 void FreeTenuredPool(void *pool_mem, size_t pool_size); 385 386 size_t GetCurrentFreeTenuredSize() const; 387 388 size_t GetCurrentFreeYoungSize() const; 389 390 private: 391 void ComputeNewYoung(); 392 void ComputeNewTenured(); 393 394 size_t GetCurrentFreeTenuredSizeUnsafe() const; 395 size_t GetCurrentFreeYoungSizeUnsafe() const; 396 397 ObjectMemorySpace young_space_; 398 size_t young_size_in_separate_pools_ {0}; 399 400 // These use for allocate shared (for young and tenured) pools and allocate blocks from them 401 size_t shared_pools_size_ {0}; 402 size_t young_size_in_shared_pools_ {0}; 403 size_t tenured_size_in_shared_pools_ {0}; 404 405 friend class panda::mem::test::HeapSpaceTest; 406 friend class panda::mem::test::MemStatsGenGCTest; 407 friend class panda::mem::test::RegionGarbageChoosingTest; 408 template <typename ObjectAllocator, bool RegularSpace> 409 friend class panda::mem::test::RegionAllocatorTestBase; 410 friend class panda::mem::test::RegionAllocatorTest; 411 friend class panda::mem::test::RegionNonmovableObjectAllocatorTest; 412 friend class panda::mem::test::RemSetTest; 413 }; 414 415 } // namespace panda::mem 416 417 #endif // PANDA_RUNTIME_MEM_HEAP_SPACE_H 418