UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
MallocBinned2.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreTypes.h"
6#include "Async/UniqueLock.h"
7#include "AutoRTFM.h"
13#include "HAL/PlatformMutex.h"
14#include "HAL/UnrealMemory.h"
15#include "Math/NumericLimits.h"
17#include "Misc/Fork.h"
18#include "Templates/Atomic.h"
19
20
21#define UE_MB2_MAX_CACHED_OS_FREES (64)
22#if PLATFORM_64BITS
23# define UE_MB2_MAX_CACHED_OS_FREES_BYTE_LIMIT (64*1024*1024)
24#else
25# define UE_MB2_MAX_CACHED_OS_FREES_BYTE_LIMIT (16*1024*1024)
26#endif
27
28#define UE_MB2_LARGE_ALLOC 65536 // Alignment of OS-allocated pointer - pool-allocated pointers will have a non-aligned pointer
29
30#if AGGRESSIVE_MEMORY_SAVING
31# define UE_MB2_MAX_SMALL_POOL_SIZE (13104) // Maximum bin size in SmallBinSizesInternal in cpp file
32# define UE_MB2_SMALL_POOL_COUNT 48
33#else
34# define UE_MB2_MAX_SMALL_POOL_SIZE (32768-16) // Maximum bin size in SmallBinSizesInternal in cpp file
35# define UE_MB2_SMALL_POOL_COUNT 51
36#endif
37
38// If we are emulating forking on a windows server or are a linux server, enable support for avoiding dirtying pages owned by the parent.
39#ifndef BINNED2_FORK_SUPPORT
40# define BINNED2_FORK_SUPPORT (UE_SERVER && (PLATFORM_UNIX || DEFAULT_SERVER_FAKE_FORKS))
41#endif
42
43#define UE_MB2_ALLOCATOR_STATS UE_MBC_ALLOCATOR_STATS
44#define UE_MB2_ALLOCATOR_STATS_VALIDATION (UE_MB2_ALLOCATOR_STATS && 0)
45
46#if UE_MB2_ALLOCATOR_STATS_VALIDATION
50#endif
51
52// Canary value used in FFreeBlock
53// A constant value unless we're compiled with fork support in which case there are two values identifying whether the page
54// was allocated pre- or post-fork
55enum class EBlockCanary : uint8
56{
57 Zero = 0x0, // Not clear why this is needed by FreeBundles
58#if BINNED2_FORK_SUPPORT
59 PreFork = 0xb7,
60 PostFork = 0xca,
61#else
62 Value = 0xe3
63#endif
64};
65
66
67//
68// Optimized virtual memory allocator.
69//
70class FMallocBinned2 : public TMallocBinnedCommon<FMallocBinned2, UE_MB2_SMALL_POOL_COUNT, UE_MB2_MAX_SMALL_POOL_SIZE>
71{
72 struct FFreeBlock;
73
74public:
75 struct FPoolTable;
76
77 struct FPoolInfo
78 {
79 enum class ECanary : uint16
80 {
81 Unassigned = 0x3941,
83 FirstFreeBlockIsPtr = 0xf317
84 };
85
86 uint16 Taken; // Number of allocated elements in this pool, when counts down to zero can free the entire pool
87 ECanary Canary; // See ECanary
88 uint32 AllocSize; // Number of bytes allocated
89 FFreeBlock* FirstFreeBlock; // Pointer to first free memory in this pool or the OS Allocation Size in bytes if this allocation is not binned
90 FPoolInfo* Next; // Pointer to next pool
91 FPoolInfo** PtrToPrevNext; // Pointer to whichever pointer points to this pool
92
93 FPoolInfo();
94
95 void CheckCanary(ECanary ShouldBe) const;
97
98 bool HasFreeBin() const;
99 void* AllocateBin();
100
104
105 void Link(FPoolInfo*& PrevNext);
106 void Unlink();
107
108 private:
109 void ExhaustPoolIfNecessary();
110 };
111
112private:
113 // Forward declares.
114 struct Private;
115
117 struct FFreeBlock
118 {
120 : BinSize(InBinSize)
121 , PoolIndex(InPoolIndex)
122 , CanaryAndForkState(InCanary)
123 , NextFreeBlock(nullptr)
124 {
126 NumFreeBins = InPageSize / InBinSize;
127 if (NumFreeBins * InBinSize + sizeof(FFreeBlock) > InPageSize)
128 {
129 NumFreeBins--;
130 }
131 check(NumFreeBins * InBinSize + sizeof(FFreeBlock) <= InPageSize);
132 }
133
134 FORCEINLINE uint32 GetNumFreeBins() const
135 {
136 return NumFreeBins;
137 }
138
139 inline void* AllocateBin()
140 {
141 --NumFreeBins;
142 // this is a pointer to a whole 64KB block
143 if (IsAligned(this, UE_MB2_LARGE_ALLOC))
144 {
145 return (uint8*)this + UE_MB2_LARGE_ALLOC - (NumFreeBins + 1) * BinSize;
146 }
147
148 // this is a pointer to a single bin within the 64KB block
149 return (uint8*)this + (NumFreeBins * BinSize);
150 }
151
152 uint16 BinSize; // Size of the bins that this list points to
153 uint8 PoolIndex; // Index of this pool
154
155 // Normally this value just functions as a canary to detect invalid memory state.
156 // When process forking is supported, it's still a canary but it has two valid values.
157 // One value is used pre-fork and one post-fork and the value is used to avoid freeing memory in pages shared with the parent process.
158 EBlockCanary CanaryAndForkState;
159
160 uint32 NumFreeBins; // Number of consecutive free bins here, at least 1.
161 FFreeBlock* NextFreeBlock; // Next free block in another pool
162 };
163
164 struct FPoolList
165 {
166 FPoolList() = default;
167
168 void Clear();
169 bool IsEmpty() const;
170
171 FPoolInfo& GetFrontPool();
172 const FPoolInfo& GetFrontPool() const;
173
174 void LinkToFront(FPoolInfo* Pool);
175
176 FPoolInfo& PushNewPoolToFront(FMallocBinned2& Allocator, FPoolTable& Table, uint32 InPoolIndex);
177
178 void ValidateActivePools() const;
179 void ValidateExhaustedPools() const;
180
181 private:
182 FPoolInfo* Front = nullptr;
183 };
184
185public:
188 {
189 FPoolList ActivePools;
190 FPoolList ExhaustedPools;
192
193#if UE_MB2_ALLOCATOR_STATS
194 uint32 TotalUsedBins = 0; // Used to calculated load factor, i.e.:
195 uint32 TotalAllocatedBins = 0; // used bins divided by total bins number in all allocated blocks
197#endif
198
200
201 FPoolTable() = default;
202 };
203
204 // Pool tables for different pool sizes
206
207private:
208#if BINNED2_FORK_SUPPORT
209 EBlockCanary CurrentCanary = EBlockCanary::PreFork; // The value of the canary for pages we have allocated this side of the fork
210 EBlockCanary OldCanary = EBlockCanary::PreFork; // If we have forked, the value canary of old pages we should avoid touching
211#else
212 static constexpr EBlockCanary CurrentCanary = EBlockCanary::Value;
213#endif
214
215#if !PLATFORM_UNIX && !PLATFORM_ANDROID
216# if UE_USE_VERYLARGEPAGEALLOCATOR
217 FCachedOSVeryLargePageAllocator CachedOSPageAllocator;
218# else
220# endif
221#else
222 FPooledVirtualMemoryAllocator CachedOSPageAllocator;
223#endif
224
225 FORCEINLINE bool IsOSAllocation(const void* Ptr) const
226 {
227#if UE_USE_VERYLARGEPAGEALLOCATOR && !PLATFORM_UNIX && !PLATFORM_ANDROID
228 return !CachedOSPageAllocator.IsSmallBlockAllocation(Ptr) && IsAligned(Ptr, UE_MB2_LARGE_ALLOC);
229#else
230 return IsAligned(Ptr, UE_MB2_LARGE_ALLOC);
231#endif
232 }
233
234 static FORCEINLINE FFreeBlock* GetPoolHeaderFromPointer(void* Ptr)
235 {
236 return (FFreeBlock*)AlignDown(Ptr, UE_MB2_LARGE_ALLOC);
237 }
238
239public:
241 virtual ~FMallocBinned2();
242
243 // FMalloc interface.
244 virtual bool IsInternallyThreadSafe() const override;
245
247 virtual void* Malloc(SIZE_T Size, uint32 Alignment) override;
248
250 virtual void* Realloc(void* Ptr, SIZE_T NewSize, uint32 Alignment) override;
251
253 virtual void Free(void* Ptr) override;
254
255 inline bool GetSmallAllocationSize(void* Ptr, SIZE_T& SizeOut) const
256 {
257 if (!IsOSAllocation(Ptr))
258 {
259 const FFreeBlock* Free = GetPoolHeaderFromPointer(Ptr);
261 SizeOut = Free->BinSize;
262 return true;
263 }
264 return false;
265 }
266
267 inline virtual bool GetAllocationSize(void *Ptr, SIZE_T &SizeOut) override
268 {
270 {
271 return true;
272 }
274 }
275
276 FORCEINLINE virtual SIZE_T QuantizeSize(SIZE_T Count, uint32 Alignment) override
277 {
278 return QuantizeSizeCommon(Count, Alignment, *this);
279 }
280
281 virtual bool ValidateHeap() override;
282 virtual void Trim(bool bTrimThreadCaches) override;
283 virtual const TCHAR* GetDescriptiveName() override;
284 virtual void UpdateStats() override;
285 virtual void OnMallocInitialized() override;
286 virtual void OnPreFork() override;
287 virtual void OnPostFork() override;
289 {
290 return CachedOSPageAllocator.GetCachedImmediatelyFreeable();
291 }
292 virtual uint64 GetTotalFreeCachedMemorySize() const override
293 {
294 return CachedOSPageAllocator.GetCachedFreeTotal();
295 }
296 // End FMalloc interface.
297
298 void* MallocExternalSmall(SIZE_T Size, uint32 Alignment);
299 void* MallocExternalLarge(SIZE_T Size, uint32 Alignment);
300
301 void CanaryFail(const FFreeBlock* Block) const;
302 inline void CanaryTest(const FFreeBlock* Block) const
303 {
304#if BINNED2_FORK_SUPPORT
305 // When we support forking there are two valid canary values.
306 if (Block->CanaryAndForkState != CurrentCanary && Block->CanaryAndForkState != OldCanary)
307#else
308 if (Block->CanaryAndForkState != CurrentCanary)
309#endif
310 {
312 }
313 }
314
316 virtual void DumpAllocatorStats(class FOutputDevice& Ar) override;
317
320 // Mapping of sizes to small table indices
322
323 static void* AllocateMetaDataMemory(SIZE_T Size);
324 static void FreeMetaDataMemory(void* Ptr, SIZE_T Size);
325
327 {
328 return SmallBinSizes[PoolIndex];
329 }
330
331 void FreeBundles(FBundleNode* Bundles, uint32 PoolIndex);
332
334};
335
336#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_7
337# include "HAL/CriticalSection.h"
338# if UE_MB2_ALLOCATOR_STATS_VALIDATION
339# include "Misc/ScopeLock.h"
340# endif
341#endif
constexpr T AlignDown(T Val, uint64 Alignment)
Definition AlignmentTemplates.h:34
constexpr bool IsAligned(T Val, uint64 Alignment)
Definition AlignmentTemplates.h:50
#define FORCEINLINE
Definition AndroidPlatform.h:140
#define check(expr)
Definition AssertionMacros.h:314
#define UE_AUTORTFM_NOAUTORTFM
Definition AutoRTFMDefines.h:113
FPlatformTypes::SIZE_T SIZE_T
An unsigned integer the same size as a pointer, the same as UPTRINT.
Definition Platform.h:1150
FPlatformTypes::TCHAR TCHAR
Either ANSICHAR or WIDECHAR, depending on whether the platform supports wide characters or the requir...
Definition Platform.h:1135
FPlatformTypes::int64 int64
A 64-bit signed integer.
Definition Platform.h:1127
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
FPlatformTypes::UPTRINT UPTRINT
An unsigned integer the same size as a pointer.
Definition Platform.h:1146
FPlatformTypes::uint64 uint64
A 64-bit unsigned integer.
Definition Platform.h:1117
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define UE_MB2_MAX_SMALL_POOL_SIZE
Definition MallocBinned2.h:34
#define UE_MB2_LARGE_ALLOC
Definition MallocBinned2.h:28
#define UE_MB2_SMALL_POOL_COUNT
Definition MallocBinned2.h:35
EBlockCanary
Definition MallocBinned2.h:56
#define UE_MBC_BIN_SIZE_SHIFT
Definition MallocBinnedCommon.h:49
#define MAX_uint16
Definition NumericLimits.h:20
#define MAX_uint8
Definition NumericLimits.h:19
uint32 Size
Definition VulkanMemory.cpp:4034
uint8_t uint8
Definition binka_ue_file_header.h:8
uint16_t uint16
Definition binka_ue_file_header.h:7
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition MallocBinned2.h:71
bool GetSmallAllocationSize(void *Ptr, SIZE_T &SizeOut) const
Definition MallocBinned2.h:255
void CanaryTest(const FFreeBlock *Block) const
Definition MallocBinned2.h:302
virtual void DumpAllocatorStats(class FOutputDevice &Ar) override
Definition MallocBinned2.cpp:904
void FlushCurrentThreadCacheInternal(bool bNewEpochOnly=false)
Definition MallocBinned2.cpp:890
void * MallocExternalLarge(SIZE_T Size, uint32 Alignment)
Definition MallocBinned2.cpp:613
void * MallocExternalSmall(SIZE_T Size, uint32 Alignment)
Definition MallocBinned2.cpp:543
virtual void OnPreFork() override
Definition MallocBinned2.cpp:465
FMallocBinned2()
Definition MallocBinned2.cpp:384
virtual ~FMallocBinned2()
Definition MallocBinned2.cpp:440
virtual void Trim(bool bTrimThreadCaches) override
Definition MallocBinned2.cpp:875
virtual bool GetAllocationSize(void *Ptr, SIZE_T &SizeOut) override
Definition MallocBinned2.h:267
virtual bool ValidateHeap() override
Definition MallocBinned2.cpp:851
void CanaryFail(const FFreeBlock *Block) const
Definition MallocBinned2.cpp:895
virtual FORCEINLINE SIZE_T QuantizeSize(SIZE_T Count, uint32 Alignment) override
Definition MallocBinned2.h:276
virtual uint64 GetImmediatelyFreeableCachedMemorySize() const override
Definition MallocBinned2.h:288
virtual void UpdateStats() override
Definition MallocBinned2.cpp:950
FPoolTable SmallPoolTables[UE_MB2_SMALL_POOL_COUNT]
Definition MallocBinned2.h:205
virtual const TCHAR * GetDescriptiveName() override
Definition MallocBinned2.cpp:865
static uint8 MemSizeToPoolIndex[1+(UE_MB2_MAX_SMALL_POOL_SIZE > > UE_MBC_BIN_SIZE_SHIFT)]
Definition MallocBinned2.h:101
virtual UE_AUTORTFM_NOAUTORTFM void * Realloc(void *Ptr, SIZE_T NewSize, uint32 Alignment) override
Definition MallocBinned2.cpp:660
static uint16 SmallBinSizes[UE_MB2_SMALL_POOL_COUNT]
Definition MallocBinned2.h:98
virtual void OnMallocInitialized() override
Definition MallocBinned2.cpp:444
static void FreeMetaDataMemory(void *Ptr, SIZE_T Size)
Definition MallocBinned2.cpp:968
static FMallocBinned2 * MallocBinned2
Definition MallocBinned2.h:319
FORCEINLINE uint32 PoolIndexToBinSize(uint32 PoolIndex) const
Definition MallocBinned2.h:326
void FreeBundles(FBundleNode *Bundles, uint32 PoolIndex)
Definition MallocBinned2.cpp:870
virtual bool IsInternallyThreadSafe() const override
Definition MallocBinned2.cpp:516
virtual void OnPostFork() override
Definition MallocBinned2.cpp:491
static void * AllocateMetaDataMemory(SIZE_T Size)
Definition MallocBinned2.cpp:961
virtual UE_AUTORTFM_NOAUTORTFM void * Malloc(SIZE_T Size, uint32 Alignment) override
Definition MallocBinned2.cpp:526
virtual uint64 GetTotalFreeCachedMemorySize() const override
Definition MallocBinned2.h:292
Definition OutputDevice.h:133
Definition MallocBinnedCommon.h:452
SIZE_T QuantizeSizeCommon(SIZE_T Count, uint32 Alignment, const FMallocBinned2 &Alloc) const
Definition MallocBinnedCommon.h:836
bool GetAllocationSizeExternal(void *Ptr, SIZE_T &SizeOut)
Definition MallocBinnedCommon.h:909
Definition OverriddenPropertySet.cpp:45
@ Front
Definition GeoEnum.h:84
FPThreadsRecursiveMutex FPlatformRecursiveMutex
Definition AndroidPlatformMutex.h:12
Definition MallocBinned2.h:78
FPoolInfo * Next
Definition MallocBinned2.h:90
uint32 AllocSize
Definition MallocBinned2.h:88
ECanary Canary
Definition MallocBinned2.h:87
uint16 Taken
Definition MallocBinned2.h:86
FFreeBlock * FirstFreeBlock
Definition MallocBinned2.h:89
void * AllocateBin()
Definition MallocBinned2.cpp:172
SIZE_T GetOSRequestedBytes() const
Definition MallocBinned2.cpp:181
void CheckCanary(ECanary ShouldBe) const
Definition MallocBinned2.cpp:121
void Unlink()
Definition MallocBinned2.cpp:214
SIZE_T GetOsAllocatedBytes() const
Definition MallocBinned2.cpp:186
FPoolInfo()
Definition MallocBinned2.cpp:111
bool HasFreeBin() const
Definition MallocBinned2.cpp:166
void SetCanary(ECanary ShouldBe, bool bPreexisting, bool bGuaranteedToBeNew)
Definition MallocBinned2.cpp:129
void SetOSAllocationSizes(SIZE_T InRequestedBytes, UPTRINT InAllocatedBytes)
Definition MallocBinned2.cpp:192
ECanary
Definition MallocBinned2.h:80
FPoolInfo ** PtrToPrevNext
Definition MallocBinned2.h:91
Definition MallocBinned2.h:188
FPoolList ExhaustedPools
Definition MallocBinned2.h:190
UE::FPlatformRecursiveMutex Mutex
Definition MallocBinned2.h:199
FPoolList ActivePools
Definition MallocBinned2.h:189
uint32 BinSize
Definition MallocBinned2.h:191
Definition PooledVirtualMemoryAllocator.h:61
Definition CachedOSPageAllocator.h:37