1 /** 2 * Copyright (c) 2021-2025 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 ark::mem { 26 27 namespace test { 28 class HeapSpaceTest; 29 class MemStatsGenGCTest; 30 template <typename ObjectAllocator, bool REGULAR_SPACE> 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 initialSize, size_t maxSize, uint32_t minFreePercentage, uint32_t maxFreePercentage); 86 87 /// @brief Compute new size of heap space 88 virtual void ComputeNewSize(); 89 90 /// @brief Get current used heap size 91 virtual size_t GetHeapSize() const; 92 93 /// @brief Try to allocate pool via PoolManager 94 [[nodiscard]] virtual Pool TryAllocPool(size_t poolSize, SpaceType spaceType, AllocatorType allocatorType, 95 void *allocatorPtr); 96 97 /// @brief Try to allocate arena via PoolManager 98 [[nodiscard]] virtual Arena *TryAllocArena(size_t arenaSize, SpaceType spaceType, AllocatorType allocatorType, 99 void *allocatorPtr); 100 101 /// @brief Free pool via PoolManager 102 void FreePool(void *poolMem, size_t poolSize, bool releasePages = true); 103 104 /// @brief Free arena via PoolManager 105 void FreeArena(Arena *arena); 106 GetMinFreePercentage()107 double GetMinFreePercentage() const 108 { 109 return minFreePercentage_; 110 } 111 GetMaxFreePercentage()112 double GetMaxFreePercentage() const 113 { 114 return maxFreePercentage_; 115 } 116 SetIsWorkGC(bool value)117 void SetIsWorkGC(bool value) 118 { 119 isWorkGc_ = value; 120 } 121 122 /// Clamp current maximum heap size as maximum possible heap size 123 void ClampCurrentMaxHeapSize(); 124 125 protected: 126 class ObjectMemorySpace { 127 public: 128 explicit ObjectMemorySpace() = default; 129 DEFAULT_COPY_SEMANTIC(ObjectMemorySpace); 130 DEFAULT_NOEXCEPT_MOVE_SEMANTIC(ObjectMemorySpace); 131 ~ObjectMemorySpace() = default; 132 133 void Initialize(size_t initialSize, size_t maxSize); 134 GetCurrentSize()135 size_t GetCurrentSize() const 136 { 137 return currentSize_; 138 } 139 GetMaxSize()140 size_t GetMaxSize() const 141 { 142 return maxSize_; 143 } 144 GetMinSize()145 size_t GetMinSize() const 146 { 147 return minSize_; 148 } 149 SetMaxSize(size_t size)150 void SetMaxSize(size_t size) 151 { 152 maxSize_ = size; 153 } 154 155 void ClampNewMaxSize(size_t newMaxSize); 156 GetCurrentNonOccupiedSize()157 size_t GetCurrentNonOccupiedSize() const 158 { 159 return maxSize_ - currentSize_; 160 } 161 UseFullSpace()162 void UseFullSpace() 163 { 164 currentSize_ = maxSize_; 165 } 166 167 /// @brief Compute new size of heap space 168 void ComputeNewSize(size_t freeBytes, double minFreePercentage, double maxFreePercentage); 169 170 /** 171 * @brief Increase current heap space 172 * @param bytes bytes count for heap space increasing 173 */ 174 void IncreaseBy(uint64_t bytes); 175 176 /** 177 * @brief Reduce current heap space 178 * @param bytes bytes count for heap space reducing 179 */ 180 void ReduceBy(size_t bytes); 181 182 // If we have too big pool for allocation then after space increasing we can have not memory for this pool, 183 // so we save pool size and increase heap space to allocate such pool 184 size_t savedPoolSize {0}; // NOLINT(misc-non-private-member-variables-in-classes) 185 186 private: 187 // min_size_ <= current_size_ <= max_size_ 188 size_t currentSize_ {0}; 189 size_t minSize_ {0}; 190 size_t maxSize_ {0}; 191 }; 192 193 // For avoid zero division 194 static constexpr uint32_t MAX_FREE_PERCENTAGE = 99; 195 196 void InitializePercentages(uint32_t minFreePercentage, uint32_t maxFreePercentage); 197 198 [[nodiscard]] Pool TryAllocPoolBase(size_t poolSize, SpaceType spaceType, AllocatorType allocatorType, 199 void *allocatorPtr, size_t currentFreeBytesInSpace, ObjectMemorySpace *memSpace, 200 OSPagesAllocPolicy allocPolicy = OSPagesAllocPolicy::NO_POLICY); 201 202 [[nodiscard]] Arena *TryAllocArenaBase(size_t arenaSize, SpaceType spaceType, AllocatorType allocatorType, 203 void *allocatorPtr, size_t currentFreeBytesInSpace, 204 ObjectMemorySpace *memSpace); 205 206 /** 207 * @brief Check that we can to allocate requested size into target space 208 * 209 * @param pool_size size of memory chunk for allocation 210 * @param current_free_bytes_in_space current freebytes count into target space 211 * @param mem_space target space in which there will be allocation 212 * 213 * @return std::optional value. If result has value then it contains bytes count for heap increasing (need during 214 * GC, for example), else need to trigger GC 215 */ 216 std::optional<size_t> WillAlloc(size_t poolSize, size_t currentFreeBytesInSpace, 217 const ObjectMemorySpace *memSpace) const; 218 219 /// @return current maximum size of this heap space 220 size_t GetCurrentSize() const; 221 222 /// @return free bytes count for current heap space size 223 size_t GetCurrentFreeBytes(size_t bytesNotInThisSpace = 0) const; 224 IsWorkGC()225 inline bool IsWorkGC() const 226 { 227 return isWorkGc_; 228 } 229 230 mutable os::memory::RWLock heapLock_; // NOLINT(misc-non-private-member-variables-in-classes) 231 ObjectMemorySpace memSpace_; // NOLINT(misc-non-private-member-variables-in-classes) 232 bool isInitialized_ {false}; // NOLINT(misc-non-private-member-variables-in-classes) 233 234 private: 235 // if GC wants allocate memory, but we have not needed memory for this then we increase current heap space 236 bool isWorkGc_ {false}; 237 double minFreePercentage_ {0}; 238 double maxFreePercentage_ {0}; 239 240 friend class ark::mem::test::RegionGarbageChoosingTest; 241 template <typename ObjectAllocator, bool REGULAR_SPACE> 242 friend class ark::mem::test::RegionAllocatorTestBase; 243 friend class ark::mem::test::RegionAllocatorTest; 244 friend class ark::mem::test::RegionNonmovableObjectAllocatorTest; 245 friend class ark::mem::test::RemSetTest; 246 friend class ark::mem::test::HeapSpaceTest; 247 }; 248 249 // Y - memory chunk is used for objects in young space 250 // T - memory chunk is used for objects in tenured space 251 // F - memory chunk which is not used for any space 252 // 253 // +---------------------------------------------------------------------------------------------------------+ 254 // | Real object memory chunks location allocating via PoolManager | 255 // |---------------------------------------------------------------------------------------------------------| 256 // | shared pool (use for G1-GC) young, tenured and free pools | 257 // |-----------------------------|---------------------------------------------------------------------------| 258 // |~~~| | |~~~|~~~| |~~~~~| |~~~~~~~~~| |~~~~~~~~~~~~~| | 259 // |~~~| | |~~~|~~~| |~~~~~| F |~~~~~~~~~| F |~~~~~~~~~~~~~| | 260 // |~Y~| F | F |~Y~|~T~| F |~~Y~~| (free |~~~~T~~~~| (free |~~~~~~T~~~~~~| non-occupied memory (F) | 261 // |~~~| | |~~~|~~~| |~~~~~| pool |~~~~~~~~~| pool |~~~~~~~~~~~~~| | 262 // |~~~| | |~~~|~~~| |~~~~~| map) |~~~~~~~~~| map) |~~~~~~~~~~~~~| | 263 // +---------------------------------------------------------------------------------------------------------+ 264 // ^ | | | | | | ^ ^ 265 // | | | | | | +----------+ | | 266 // | | +-----------+ | | | | | | 267 // | | | +----------|----------+ +--------------+ | all occupied memory via PoolManager | 268 // | | | | +-------+ | +------------+ | 269 // | | | | | | | | 270 // v v v v v v v v 271 // +---------------------------|-----------------------------------------------------------------------------+ 272 // |~~~|~~~|~~~~~| | |~~~|~~~~~~~|~~~~~~~~~~~~~| | | 273 // |~~~|~~~|~~~~~| | |~~~|~~~~~~~|~~~~~~~~~~~~~| | | 274 // |~Y~|~Y~|~~Y~~| F | F |~T~|~~~T~~~|~~~~~~T~~~~~~| F | F | 275 // |~~~|~~~|~~~~~| | |~~~|~~~~~~~|~~~~~~~~~~~~~| | | 276 // |~~~|~~~|~~~~~| | |~~~|~~~~~~~|~~~~~~~~~~~~~| | | 277 // |-------------|-----|-------|-------------------------|-------------------------|-------------------------| 278 // | used | | | used | | | 279 // |-------------------|-------|---------------------------------------------------|-------------------------| 280 // | current max size | | current max size | | 281 // |---------------------------|-----------------------------------------------------------------------------| 282 // | Young space | Tenured space | 283 // |---------------------------|-----------------------------------------------------------------------------| 284 // | young-space-size object-memory-pool 285 // | argument argument 286 // |---------------------------------------------------------------------------------------------------------| 287 // | Generations memory representation | 288 // +---------------------------------------------------------------------------------------------------------+ 289 // 290 // init-young-space-size argument set minimum (initial) current max size for Young space 291 // init-object-memory-pool argument set minimum (initial) current max size for heap 292 /** 293 * Class for description of object spaces and limits (minimum, maximum, current sizes) for generatinal-based GC. 294 * Pools for young and tenured spaces is allocated via this class. Also this class cooperate with PoolManager for info 295 * getting about object spaces. If current max size is not enough for new pool in some space then need to trigger GC 296 */ 297 class GenerationalSpaces final : public HeapSpace { 298 public: 299 explicit GenerationalSpaces() = default; 300 NO_COPY_SEMANTIC(GenerationalSpaces); 301 NO_MOVE_SEMANTIC(GenerationalSpaces); 302 ~GenerationalSpaces() override = default; 303 304 void Initialize(size_t initialYoungSize, bool wasSetInitialYoungSize, size_t maxYoungSize, bool wasSetMaxYoungSize, 305 size_t initialTotalSize, size_t maxTotalSize, uint32_t minFreePercentage, 306 uint32_t maxFreePercentage); 307 308 /// @brief Compute new sizes for young and tenured spaces 309 void ComputeNewSize() override; 310 311 void UpdateSize(size_t desiredYoungSize); 312 313 size_t GetHeapSize() const override; 314 315 size_t UpdateYoungSpaceMaxSize(size_t size); 316 // Use for CanAllocInSpace 317 static constexpr bool IS_YOUNG_SPACE = true; 318 static constexpr bool IS_TENURED_SPACE = false; 319 320 /** 321 * @return true if we can allocate requested size in space 322 * 323 * @param is_young young or tenured space 324 * @param chunk_size memory size for allocating in space 325 */ 326 bool CanAllocInSpace(bool isYoung, size_t chunkSize) const; 327 328 size_t GetCurrentYoungSize() const; 329 330 size_t GetMaxYoungSize() const; 331 332 void UseFullYoungSpace(); 333 334 /// @brief Allocate alone young pool (use for Gen-GC) with maximum possible size of young space 335 [[nodiscard]] Pool AllocAlonePoolForYoung(SpaceType spaceType, AllocatorType allocatorType, void *allocatorPtr); 336 337 /** 338 * @brief Try allocate pool for young space (use for G1-GC). 339 * If free size in young space less requested pool size then pool can be allocate only during GC work 340 */ 341 [[nodiscard]] Pool TryAllocPoolForYoung(size_t poolSize, SpaceType spaceType, AllocatorType allocatorType, 342 void *allocatorPtr); 343 344 /** 345 * @brief Try allocate pool for tenured space (use for generational-based GC). 346 * If free size in tenured space less requested pool size then pool can be allocate only during GC work 347 */ 348 [[nodiscard]] Pool TryAllocPoolForTenured(size_t poolSize, SpaceType spaceType, AllocatorType allocatorType, 349 void *allocatorPtr, 350 OSPagesAllocPolicy allocPolicy = OSPagesAllocPolicy::NO_POLICY); 351 352 [[nodiscard]] Pool TryAllocPool(size_t poolSize, SpaceType spaceType, AllocatorType allocatorType, 353 void *allocatorPtr) final; 354 355 [[nodiscard]] Arena *TryAllocArenaForTenured(size_t arenaSize, SpaceType spaceType, AllocatorType allocatorType, 356 void *allocatorPtr); 357 358 [[nodiscard]] Arena *TryAllocArena(size_t arenaSize, SpaceType spaceType, AllocatorType allocatorType, 359 void *allocatorPtr) final; 360 361 /// @brief Allocate pool shared usage between young and tenured spaces (use for regions in G1-GC). 362 [[nodiscard]] Pool AllocSharedPool(size_t poolSize, SpaceType spaceType, AllocatorType allocatorType, 363 void *allocatorPtr); 364 365 void IncreaseYoungOccupiedInSharedPool(size_t chunkSize); 366 367 void IncreaseTenuredOccupiedInSharedPool(size_t chunkSize); 368 369 void ReduceYoungOccupiedInSharedPool(size_t chunkSize); 370 371 void ReduceTenuredOccupiedInSharedPool(size_t chunkSize); 372 373 void FreeSharedPool(void *poolMem, size_t poolSize); 374 375 void FreeYoungPool(void *poolMem, size_t poolSize, bool releasePages = true); 376 377 void PromoteYoungPool(size_t poolSize); 378 379 void FreeTenuredPool(void *poolMem, size_t poolSize, bool releasePages = true); 380 381 size_t GetCurrentFreeTenuredSize() const; 382 383 size_t GetCurrentFreeYoungSize() const; 384 385 private: 386 void ComputeNewYoung(); 387 void UpdateYoungSize(size_t desiredYoungSize); 388 void ComputeNewTenured(); 389 390 size_t GetCurrentFreeTenuredSizeUnsafe() const; 391 size_t GetCurrentFreeYoungSizeUnsafe() const; 392 393 ObjectMemorySpace youngSpace_; 394 size_t youngSizeInSeparatePools_ {0}; 395 396 // These use for allocate shared (for young and tenured) pools and allocate blocks from them 397 size_t sharedPoolsSize_ {0}; 398 size_t youngSizeInSharedPools_ {0}; 399 size_t tenuredSizeInSharedPools_ {0}; 400 401 friend class ark::mem::test::HeapSpaceTest; 402 friend class ark::mem::test::MemStatsGenGCTest; 403 friend class ark::mem::test::RegionGarbageChoosingTest; 404 template <typename ObjectAllocator, bool REGULAR_SPACE> 405 friend class ark::mem::test::RegionAllocatorTestBase; 406 friend class ark::mem::test::RegionAllocatorTest; 407 friend class ark::mem::test::RegionNonmovableObjectAllocatorTest; 408 friend class ark::mem::test::RemSetTest; 409 }; 410 411 } // namespace ark::mem 412 413 #endif // PANDA_RUNTIME_MEM_HEAP_SPACE_H 414