FMallocBinned内存分配器
FMallocBinned是虚幻引擎实现的第一代装箱内存分配器,其重要的配置参数及成员变量如下:
#if PLATFORM_IOS // IOS平台 #define PLAT_PAGE_SIZE_LIMIT 16384 #define PLAT_BINNED_ALLOC_POOLSIZE 16384 #define PLAT_SMALL_BLOCK_POOL_SIZE 256 #else // 非IOS平台 #define PLAT_PAGE_SIZE_LIMIT 65536 #define PLAT_BINNED_ALLOC_POOLSIZE 65536 #define PLAT_SMALL_BLOCK_POOL_SIZE 0 #endif
// --------------------------------------------------------------------
struct FMallocBinned::Private { /** Default alignment for binned allocator */ enum { DEFAULT_BINNED_ALLOCATOR_ALIGNMENT = sizeof(FFreeMem) }; // sizeof(FFreeMem)=16 static_assert(DEFAULT_BINNED_ALLOCATOR_ALIGNMENT == 16, "Default alignment should be 16 bytes"); enum { PAGE_SIZE_LIMIT = PLAT_PAGE_SIZE_LIMIT }; // BINNED_ALLOC_POOL_SIZE can be increased beyond 64k to cause binned malloc to allocate // the small size bins in bigger chunks. If OS Allocation is slow, increasing // this number *may* help performance but YMMV. enum { BINNED_ALLOC_POOL_SIZE = PLAT_BINNED_ALLOC_POOLSIZE }; // On IOS can push small allocs in to a pre-allocated small block pool enum { SMALL_BLOCK_POOL_SIZE = PLAT_SMALL_BLOCK_POOL_SIZE }; // ... ... }; // ... ... class FMallocBinned : public FMalloc { struct Private; private: // Counts. enum { POOL_COUNT = 41 }; /** Maximum allocation for the pooled allocator */ enum { EXTENDED_PAGE_POOL_ALLOCATION_COUNT = 2 }; enum { MAX_POOLED_ALLOCATION_SIZE = 32768+1 }; // ... ... FCriticalSection AccessGuard; // 用于FScopeLock的临界段对象,实现对临界段的互斥访问 // ... ... FPoolTable PoolTable[POOL_COUNT]; // 所有的内存池表列表, 单个内存池的Block尺寸是一样的 FPoolTable OsTable; // 未使用 FPoolTable PagePoolTable[EXTENDED_PAGE_POOL_ALLOCATION_COUNT]; // 非小块内存的内存池表. FPoolTable* MemSizeToPoolTable[MAX_POOLED_ALLOCATION_SIZE+EXTENDED_PAGE_POOL_ALLOCATION_COUNT]; // 根据size索引的内存池表, 实际会指向PoolTable和PagePoolTable PoolHashBucket* HashBuckets; // Key命中时使用的内存池哈希桶 PoolHashBucket* HashBucketFreeList; // Key未命中时使用的内存池哈希桶 uint32 PageSize; // 内存页大小 // ... ... };
FMallocBinned(Android平台)的各成员变量数值如下:
注:BinnedSizeLimit != PageSize / 2,因此2个扩展的内存池PagePoolTable的BlockSize为0,相当于被弃用
FMallocBinned(IOS平台)的各成员变量数值如下:
FMallocBinned(Windows平台)的各成员变量数值如下:
注1:Windows平台,PageSize被传入的GetConstants().BinnedPageSize赋值
注2:PageSize填为64KB,主要是因为VirtualAlloc保留的虚拟内存地址空间的起始地址必须是64KB整数倍,填其他值会产生虚拟内存地址碎片,造成浪费
FMallocBinned内存分配器的初始化
// 在其构造函数中
通过Size的大小,来找到合适的FPoolTable。为了查找的过程不会浪费性能,UE4提前建立了32KB的地址空间MemSizeToPoolTable到相应内存池表PoolTable的映射。
FMallocBinned::FMallocBinned(uint32 InPageSize, uint64 AddressLimit) { // ... ... /** Shift to get the reference from the indirect tables */ PoolBitShift = FPlatformMath::CeilLogTwo(PageSize); IndirectPoolBitShift = FPlatformMath::CeilLogTwo(PageSize/sizeof(FPoolInfo)); IndirectPoolBlockSize = PageSize/sizeof(FPoolInfo); MaxHashBuckets = AddressLimit >> (IndirectPoolBitShift+PoolBitShift); MaxHashBucketBits = FPlatformMath::CeilLogTwo(MaxHashBuckets); MaxHashBucketWaste = (MaxHashBuckets*sizeof(PoolHashBucket))/1024; MaxBookKeepingOverhead = ((AddressLimit/PageSize)*sizeof(PoolHashBucket))/(1024*1024); /** * Shift required to get required hash table key. */ HashKeyShift = PoolBitShift+IndirectPoolBitShift; /** Used to mask off the bits that have been used to lookup the indirect table */ PoolMask = ( ( 1ull << ( HashKeyShift - PoolBitShift ) ) - 1 ); // 装箱的最大尺寸为8k(IOS)或32k(非IOS平台). BinnedSizeLimit = Private::PAGE_SIZE_LIMIT/2; BinnedOSTableIndex = BinnedSizeLimit+EXTENDED_PAGE_POOL_ALLOCATION_COUNT; check((BinnedSizeLimit & (BinnedSizeLimit-1)) == 0); // Init tables. OsTable.FirstPool = nullptr; OsTable.ExhaustedPool = nullptr; OsTable.BlockSize = 0; /** The following options are not valid for page sizes less than 64k. They are here to reduce waste*/ // 初始化内存页的内存池1, 默认情况下, 它的BlockSize为12k(IOS)或48k(非IOS平台). PagePoolTable[0].FirstPool = nullptr; PagePoolTable[0].ExhaustedPool = nullptr; PagePoolTable[0].BlockSize = PageSize == Private::PAGE_SIZE_LIMIT ? BinnedSizeLimit+(BinnedSizeLimit/2) : 0; // 初始化内存页的内存池2, 默认情况下, 它的BlockSize为24k(IOS)或96k(非IOS平台). PagePoolTable[1].FirstPool = nullptr; PagePoolTable[1].ExhaustedPool = nullptr; PagePoolTable[1].BlockSize = PageSize == Private::PAGE_SIZE_LIMIT ? PageSize+BinnedSizeLimit : 0; // Block sizes are based around getting the maximum amount of allocations per pool, with as little alignment waste as possible. // Block sizes should be close to even divisors of the POOL_SIZE, and well distributed. They must be 16-byte aligned as well. // 用来创建不同BlockSize的数字数组, 它们遵循两个规则: 1. 尽可能是内存池尺寸的整除数(因子), 减少内存浪费; 2. 必须16位对齐. static const uint32 BlockSizes[POOL_COUNT] = { 16, 32, 48, 64, 80, 96, 112, 128, 160, 192, 224, 256, 288, 320, 384, 448, 512, 576, 640, 704, 768, 896, 1024, 1168, 1360, 1632, 2048, 2336, 2720, 3264, 4096, 4672, 5456, 6544, 8192, 9360, 10912, 13104, 16384, 21840, 32768 }; // 创建内存块的内存池表, 并根据BlockSizes初始化BlockSize for( uint32 i = 0; i < POOL_COUNT; i++ ) { PoolTable[i].FirstPool = nullptr; PoolTable[i].ExhaustedPool = nullptr; PoolTable[i].BlockSize = BlockSizes[i]; check(IsAligned(BlockSizes[i], Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT)); #if STATS PoolTable[i].MinRequest = PoolTable[i].BlockSize; #endif } // 初始化MemSizeToPoolTable, 将所有大小的内存池表指向PoolTable. for( uint32 i=0; i<MAX_POOLED_ALLOCATION_SIZE; i++ ) { uint32 Index = 0; while( PoolTable[Index].BlockSize < i ) { ++Index; } checkSlow(Index < POOL_COUNT); MemSizeToPoolTable[i] = &PoolTable[Index]; } // 将内存页的内存池表添加到MemSizeToPoolTable数组的末尾. MemSizeToPoolTable[BinnedSizeLimit] = &PagePoolTable[0]; MemSizeToPoolTable[BinnedSizeLimit+1] = &PagePoolTable[1]; check(MAX_POOLED_ALLOCATION_SIZE - 1 == PoolTable[POOL_COUNT - 1].BlockSize); }
FPoolTable
// 同一Block大小内存池表
/** 内存池表 */ struct FPoolTable { FPoolInfo* FirstPool; // 指向有空闲Block的内存池链表 注:当为空会创建出新的Pool并赋值给FirstPool,ExhaustedPool中Free内存后也会移到FirstPool链表上 FPoolInfo* ExhaustedPool; // 指向已满(没有可分配的内存)的内存池链表 注:FirstPool满了,就会移动到ExhaustedPool链表上 uint32 BlockSize; // 当前PoolTable中所有内存池的Block大小 };
1. 有空闲Block的内存池链表FirstPool
① 当FirstPool为空时, 则分配一块BINNED_ALLOC_POOL_SIZE(IOS:16KB,其他平台:64KB)的内存块来给内存池划分成Block使用
② 从FirstPool中分配Block后,如果该FPoolInfo已满,会将该FPoolInfo从FirstPool中移动到ExhaustedPool链表头部
③ Free掉Ptr指针时,如果该Ptr在FirstPool链表中某个FPoolInfo中最后一个占用内存Block,会释放该FPoolInfo占用的内存页
2. 指向已满(没有可分配的内存)的内存池链表ExhaustedPool
① Free掉Ptr指针时,如果该内存占用在ExhaustedPool链表中,会将该FPoolInfo从ExhaustedPool中移动到FirstPool链表头部
FPoolInfo
// 内存池
FPoolInfo中的所有Block为空闲时,才释放其占用的内存页
// 内存池 32 bytes. struct FMallocBinned::FPoolInfo { /** Number of allocated elements in this pool, when counts down to zero can free the entire pool. */ uint16 Taken; // 已分配的Block的个数 当为0时,将释放整个内存池及其FirstMem指向的内存块 /** Index of pool. Index into MemSizeToPoolTable[]. Valid when < MAX_POOLED_ALLOCATION_SIZE, MAX_POOLED_ALLOCATION_SIZE is OsTable. When AllocSize is 0, this is the number of pages to step back to find the base address of an allocation. See FindPoolInfoInternal() */ uint16 TableIndex; // 在MemSizeToPoolTable中的索引 /** Number of bytes allocated */ uint32 AllocSize; // 已分配的字节数 /** Pointer to first free memory in this pool or the OS Allocation Size in bytes if this allocation is not binned*/ FFreeMem* FirstMem; // 如果是Bin模式,指向内存池可用的内存块Block链表; 如果非Bin模式, 指向由操作系统直接分配的内存块. FPoolInfo* Next; // 指向下一个内存池 FPoolInfo** PrevLink; // };
FFreeMem
// 内存块
FPoolInfo的空闲内存链表
① 内存池FFreeMem* FirstMem链表尾部的FFreeMem指向分配内存块的起始处,其NumFreeBlocks为剩余空闲Block的个数(注:该个数不包括后面被free还回来的Block),并从后面向前来分配
② 被free还回来的Block的FFreeMem节点,会存放在Block所在位置,并指向该Block,其NumFreeBlocks为1,并被插入到内存池FFreeMem* FirstMem链表的头部
③ 分配Block时,从FFreeMem* FirstMem链表的前向后来分配,优先把free还回来的Block再次分配出去
/** 内存块. 16 bytes */ struct alignas(16) FMallocBinned::FFreeMem { /** Next or MemLastPool[], always in order by pool. */ FFreeMem* Next; // 释放1个Block时,会构建该Block的FFreeMem,并插入到Pool->FirstMem链表的头部 /** Number of consecutive free blocks here, at least 1. */ uint32 NumFreeBlocks; // 空闲Block个数 };
注1:alignas(16)表示struct FMallocBinned::FFreeMem的size对齐到16字节的倍数
注2:alignas(n)也可以修饰成员变量,表示该成员变量到n字节的倍数
PoolHashBucket
// 内存池哈希桶
负责分配和管理FPoolInfo,通过对Ptr指针执行Hash算法,可O(1)找到该地址对应的FPoolInfo,具体流程如下:
① 通过Ptr地址计算CalcKey=Ptr >> Allocator.HashKeyShift
② 再用CalcKey & (MaxHashBuckets - 1)来计算在PoolHashBucket* HashBuckets的索引值BucketIndex
③ 当HashBuckets[BucketIndex].Key==CalcKey,目标PoolHashBucket* TargetBucket为当前&HashBuckets[BucketIndex]
当HashBuckets[BucketIndex].Key!=CalcKey,遍历Next(链表后面的PoolHashBucket节点都存放在PoolHashBucket* HashBucketFreeList中)来查找目标PoolHashBucket* TargetBucket
④ 计算PoolIndex=((UPTRINT)Ptr >> PoolBitShift) & PoolMask,最后使用TargetBucket->FirstPool[PoolIndex]来得到Ptr地址的内存池FPoolInfo
/** 内存池哈希桶,用于存放由内存地址哈希出来的键对应的内存池链表 32 bytes */ struct FMallocBinned::PoolHashBucket { UPTRINT Key; // 哈希键 Key=Ptr >> Allocator.HashKeyShift 内存地址右移27个bit位 FPoolInfo* FirstPool; // 指向内存池内存块(大小为64KB:成员变量PageSize的值)的起始处 PoolHashBucket* Prev; // 上一个内存池哈希桶 PoolHashBucket* Next; // 下一个内存池哈希桶 };
从内存池Pool中分配内存给Block
会检查当前Block的内存池是否已满,如果已满,会将其移动到FPoolInfo* ExhaustedPool链表上
static FFreeMem* AllocateBlockFromPool(FMallocBinned& Allocator, FPoolTable* Table, FPoolInfo* Pool, uint32 Alignment) { // Pick first available block and unlink it. Pool->Taken++; checkSlow(Pool->TableIndex < Allocator.BinnedOSTableIndex); // if this is false, FirstMem is actually a size not a pointer checkSlow(Pool->FirstMem); checkSlow(Pool->FirstMem->NumFreeBlocks > 0); checkSlow(Pool->FirstMem->NumFreeBlocks < PAGE_SIZE_LIMIT); FFreeMem* Free = (FFreeMem*)((uint8*)Pool->FirstMem + --Pool->FirstMem->NumFreeBlocks * Table->BlockSize); // 从FFreeMem* FirstMem指向地址的后面向前分配 if( !Pool->FirstMem->NumFreeBlocks ) // 当前内存池的FirstMem的Block个数为0 { Pool->FirstMem = Pool->FirstMem->Next; // 将FirstMem的Next赋值给FirstMem if( !Pool->FirstMem ) // 当到达FirstMem链表的末尾,即Pool->FirstMem为空,表明Pool没有任何空闲空间了 { // Move to exhausted list. Pool->Unlink(); // 从Table->FirstPool链表上移除Pool Pool->Link( Table->ExhaustedPool ); // 从头部将Pool加入到Table->ExhaustedPool链表上 } } BINNED_PEAK_STATCOUNTER(Allocator.UsedPeak, BINNED_ADD_STATCOUNTER(Allocator.UsedCurrent, Table->BlockSize)); return Align(Free, Alignment); }
Free掉Ptr指针的内存占用
/** * Releases memory back to the system. This is not protected from multi-threaded access and it's * the callers responsibility to Lock AccessGuard before calling this. */ // 释放指针为Ptr的内存占用 static void FreeInternal(FMallocBinned& Allocator, void* Ptr) { MEM_TIME(MemTime -= FPlatformTime::Seconds()); BINNED_DECREMENT_STATCOUNTER(Allocator.CurrentAllocs); #if PLATFORM_IOS || PLATFORM_MAC if(FPlatformMemory::PtrIsOSMalloc(Ptr)) { SmallOSFree(Allocator, Ptr, Private::SMALL_BLOCK_POOL_SIZE); return; } #endif UPTRINT BasePtr; FPoolInfo* Pool = FindPoolInfo(Allocator, (UPTRINT)Ptr, BasePtr); checkSlow(Pool); checkSlow(Pool->GetBytes() != 0); if (Pool->TableIndex < Allocator.BinnedOSTableIndex) // 是否被Binned内存池管理 { FPoolTable* Table = Allocator.MemSizeToPoolTable[Pool->TableIndex]; #ifdef USE_FINE_GRAIN_LOCKS FScopeLock TableLock(&Table->CriticalSection); #endif #if STATS Table->ActiveRequests--; #endif // If this pool was exhausted, move to available list. if( !Pool->FirstMem ) // Pool在FPoolInfo* ExhaustedPool链表上 { Pool->Unlink(); // 从Table->ExhaustedPool链表上移除Pool Pool->Link( Table->FirstPool ); // 从头部将Pool加入到Table->FirstPool链表上 } void* BaseAddress = (void*)BasePtr; uint32 BlockSize = Table->BlockSize; PTRINT OffsetFromBase = (PTRINT)Ptr - (PTRINT)BaseAddress; check(OffsetFromBase >= 0); uint32 AlignOffset = OffsetFromBase % BlockSize; // Patch pointer to include previously applied alignment. Ptr = (void*)((PTRINT)Ptr - (PTRINT)AlignOffset); // Free a pooled allocation. // 在释放的Block内存区域处,构建一个FFreeMem,并插入到Pool->FirstMem链表的头部 FFreeMem* Free = (FFreeMem*)Ptr; Free->NumFreeBlocks = 1; Free->Next = Pool->FirstMem; Pool->FirstMem = Free; BINNED_ADD_STATCOUNTER(Allocator.UsedCurrent, -(int64)(Table->BlockSize)); // Free this pool. checkSlow(Pool->Taken >= 1); if( --Pool->Taken == 0 ) // 当前内存池的Taken为0时 { #if STATS Table->NumActivePools--; #endif // Free the OS memory. SIZE_T OsBytes = Pool->GetOsBytes(Allocator.PageSize, Allocator.BinnedOSTableIndex); BINNED_ADD_STATCOUNTER(Allocator.OsCurrent, -(int64)OsBytes); BINNED_ADD_STATCOUNTER(Allocator.WasteCurrent, -(int64)(OsBytes - Pool->GetBytes())); Pool->Unlink(); // 从链表上移除Pool Pool->SetAllocationSizes(0, 0, 0, Allocator.BinnedOSTableIndex); // 将Pool设置成初始值 OSFree(Allocator, (void*)BasePtr, OsBytes); // 释放内存池占用的虚拟内存页,还给OS } } else // 该内存是从OS上直接申请的 { // Free an OS allocation. checkSlow(!((UPTRINT)Ptr & (Allocator.PageSize - 1))); SIZE_T OsBytes = Pool->GetOsBytes(Allocator.PageSize, Allocator.BinnedOSTableIndex); BINNED_ADD_STATCOUNTER(Allocator.UsedCurrent, -(int64)Pool->GetBytes()); BINNED_ADD_STATCOUNTER(Allocator.OsCurrent, -(int64)OsBytes); BINNED_ADD_STATCOUNTER(Allocator.WasteCurrent, -(int64)(OsBytes - Pool->GetBytes())); OSFree(Allocator, (void*)BasePtr, OsBytes); // 释放内存池占用的虚拟内存页,还给OS } MEM_TIME(MemTime += FPlatformTime::Seconds()); }
Malloc分配内存
void* FMallocBinned::Malloc(SIZE_T Size, uint32 Alignment) { #ifdef USE_COARSE_GRAIN_LOCKS FScopeLock ScopedLock(&AccessGuard); #endif Private::FlushPendingFrees(*this); Alignment = FMath::Max<uint32>(Alignment, Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT); // Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT为sizeof(FFreeMem),为16Bytes Size = Align(Size, Alignment); // Size必须对齐到16的整数倍 MEM_TIME(MemTime -= FPlatformTime::Seconds()); BINNED_INCREMENT_STATCOUNTER(CurrentAllocs); BINNED_INCREMENT_STATCOUNTER(TotalAllocs); FFreeMem* Free = nullptr; bool bUsePools = true; // 默认使用内存池 #if USE_OS_SMALL_BLOCK_ALLOC && !USE_OS_SMALL_BLOCK_GRAB_MEMORY_FROM_OS // IOS会走这块逻辑 if (FPlatformMemory::IsNanoMallocAvailable() && Size <= Private::SMALL_BLOCK_POOL_SIZE) { //Make sure we have initialized our hash buckets even if we are using the NANO_MALLOC grabber, as otherwise we can end //up making bad assumptions and trying to grab invalid data during a Realloc of this data. if (!HashBuckets) { HashBuckets = Private::CreateHashBuckets(*this); } bUsePools = false; UPTRINT AlignedSize = Align(Size, Alignment); SIZE_T ActualPoolSize; //TODO: use this to reduce waste? Free = (FFreeMem*)Private::SmallOSAlloc(*this, AlignedSize, ActualPoolSize); check(FPlatformMemory::PtrIsOSMalloc(Free)); if(!FPlatformMemory::PtrIsFromNanoMalloc(Free)) { // This means we've overflowed the nano zone's internal buckets, which are fixed // So we need to fall back to UE's allocator Private::SmallOSFree(*this, Free, AlignedSize); bUsePools = true; Free = nullptr; } } #endif if (bUsePools) { if( Size < BinnedSizeLimit) // 申请的内存块小于装箱的最大尺寸为PAGE_SIZE_LIMIT / 2(IOS:8KB,其他平台:32KB) { // Allocate from pool. FPoolTable* Table = MemSizeToPoolTable[Size]; // 根据Size从MemSizeToPoolTable中找到FPoolTable #ifdef USE_FINE_GRAIN_LOCKS FScopeLock TableLock(&Table->CriticalSection); #endif checkSlow(Size <= Table->BlockSize); Private::TrackStats(Table, (uint32)Size); FPoolInfo* Pool = Table->FirstPool; if( !Pool ) { // 为空,则分配一块BINNED_ALLOC_POOL_SIZE(IOS:16KB,其他平台:64KB)的内存块来给内存池划分成Block使用 // 如果内存池还不存在,则分配一块IndirectPoolBlockSize * sizeof(FPoolInfo)的内存块来存放FPoolInfo,详见CreateIndirect函数 // 函数中还会创建PoolHashBucket* HashBuckets内存池哈希桶,用Ptr地址通过该哈希桶的O(1)算法找到FPoolInfo Pool = Private::AllocatePoolMemory(*this, Table, Private::BINNED_ALLOC_POOL_SIZE/*PageSize*/, Size); } // 从内存池FPoolInfo上分配空闲Block的内存 Free = Private::AllocateBlockFromPool(*this, Table, Pool, Alignment); } // IOS:如果分配的尺寸处于BinnedSizeLimit(32k)和PagePoolTable[0].BlockSize(48k)之间或者处于PageSize(64k)和PagePoolTable[1].BlockSize(96k)之间, 由PagePoolTable页内存池表中. // Android:由于BinnedSizeLimit != PageSize / 2,PagePoolTable[0].BlockSize和PagePoolTable[1].BlockSize均为0 // 其他平台:分配的尺寸处于BinnedSizeLimit(8k)和PagePoolTable[0].BlockSize(12k)之间或者处于PageSize(16k)和PagePoolTable[1].BlockSize(24k)之间 else if ( ((Size >= BinnedSizeLimit && Size <= PagePoolTable[0].BlockSize) || (Size > PageSize && Size <= PagePoolTable[1].BlockSize))) { // Bucket in a pool of 3*PageSize or 6*PageSize uint32 BinType = Size < PageSize ? 0 : 1; uint32 PageCount = 3*BinType + 3; FPoolTable* Table = &PagePoolTable[BinType]; #ifdef USE_FINE_GRAIN_LOCKS FScopeLock TableLock(&Table->CriticalSection); #endif checkSlow(Size <= Table->BlockSize); Private::TrackStats(Table, (uint32)Size); FPoolInfo* Pool = Table->FirstPool; if( !Pool ) { Pool = Private::AllocatePoolMemory(*this, Table, PageCount*PageSize, BinnedSizeLimit+BinType); } Free = Private::AllocateBlockFromPool(*this, Table, Pool, Alignment); } else // 超过了内存页尺寸, 直接由系统分配内存, 且放入HashBuckets表中 { // Use OS for large allocations. UPTRINT AlignedSize = Align(Size, PageSize); SIZE_T ActualPoolSize; //TODO: use this to reduce waste? Free = (FFreeMem*)Private::OSAlloc(*this, AlignedSize, ActualPoolSize); if (!Free) { Private::OutOfMemory(AlignedSize); } void* AlignedFree = Align(Free, Alignment); // Create indirect. FPoolInfo* Pool; { #ifdef USE_FINE_GRAIN_LOCKS FScopeLock PoolInfoLock(&AccessGuard); #endif Pool = Private::GetPoolInfo(*this, (UPTRINT)Free); if ((UPTRINT)Free != ((UPTRINT)AlignedFree & ~((UPTRINT)PageSize - 1))) { // Mark the FPoolInfo for AlignedFree to jump back to the FPoolInfo for ptr. for (UPTRINT i = (UPTRINT)PageSize, Offset = 0; i < AlignedSize; i += PageSize, ++Offset) { FPoolInfo* TrailingPool = Private::GetPoolInfo(*this, ((UPTRINT)Free) + i); check(TrailingPool); //Set trailing pools to point back to first pool TrailingPool->SetAllocationSizes(0, 0, Offset, BinnedOSTableIndex); } } } Free = (FFreeMem*)AlignedFree; Pool->SetAllocationSizes(Size, AlignedSize, BinnedOSTableIndex, BinnedOSTableIndex); BINNED_PEAK_STATCOUNTER(OsPeak, BINNED_ADD_STATCOUNTER(OsCurrent, AlignedSize)); BINNED_PEAK_STATCOUNTER(UsedPeak, BINNED_ADD_STATCOUNTER(UsedCurrent, Size)); BINNED_PEAK_STATCOUNTER(WastePeak, BINNED_ADD_STATCOUNTER(WasteCurrent, (int64)(AlignedSize - Size))); } #if USE_OS_SMALL_BLOCK_ALLOC check(!FPlatformMemory::PtrIsOSMalloc(Free)); #endif } MEM_TIME(MemTime += FPlatformTime::Seconds()); return Free; }
FMallocBinned包含POOL_COUNT(41)个内存池和2个扩展的页内存池,每个内存池中包含Block的size都是一样的。
① (0, PAGE_SIZE_LIMIT/2)共使用41个不同Block大小的池子(一个池子分配的内存大小为BINNED_ALLOC_POOL_SIZE)
OS | PAGE_SIZE_LIMIT/2 | BINNED_ALLOC_POOL_SIZE | 范围 |
非IOS | 32KB | 64KB | (0, 32KB) |
IOS | 8KB | 16KB | (0, 8KB) |
41个Block的size:
static const uint32 BlockSizes[POOL_COUNT] = // POOL_COUNT为41 { 16, 32, 48, 64, 80, 96, 112, 128, 160, 192, 224, 256, 288, 320, 384, 448, 512, 576, 640, 704, 768, 896, 1024, 1168, 1360, 1632, 2048, 2336, 2720, 3264, 4096, 4672, 5456, 6544, 8192, 9360, 10912, 13104, 16384, 21840, 32768 };
② [PAGE_SIZE_LIMIT/2, PagePoolTable[0].BlockSize]使用池子的内存大小为3*PageSize
(PageSize, PagePoolTable[1].BlockSize]使用池子的内存大小为6*PageSize
OS | PAGE_SIZE_LIMIT/2 |
PagePoolTable[0].BlockSize (BinnedSizeLimit+(BinnedSizeLimit/2)) |
PagePoolTable[1].BlockSize (PageSize+BinnedSizeLimit) |
PageSize | 范围 |
Windows | 32KB |
48KB |
96KB | 64KB | [32KB, 48KB] || (64KB, 96KB] |
Android | 32KB | 0 | 0 | 4KB | 不成立 |
IOS | 8KB | 12KB | 24KB | 16KB | [8KB, 12KB] || (16KB, 24KB] |
③ 其它待分配内存的大小直接使用系统分配,且放入HashBuckets中
FMallocBinned架构图
以Windows平台为例
内存对齐
// 详见:UnrealEngine\Engine\Source\Runtime\Core\Public\Templates\AlignmentTemplates.h
/** * Aligns a value to the nearest higher multiple of 'Alignment', which must be a power of two. * * @param Val The value to align. * @param Alignment The alignment value, must be a power of two. * * @return The value aligned up to the specified alignment. */ template <typename T> FORCEINLINE constexpr T Align(T Val, uint64 Alignment) { static_assert(TIsIntegral<T>::Value || TIsPointer<T>::Value, "Align expects an integer or pointer type"); return (T)(((uint64)Val + Alignment - 1) & ~(Alignment - 1)); }
① Malloc、Realloc、QuantizeSize函数中对Size进行内存对齐
Alignment = FMath::Max<uint32>(Alignment, Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT); // Alignment为16
Size = Align(Size, Alignment);
注:由于sizeof(FFreeMem)为16,分配的Size不能比该值小,否则连FFreeMem都放不下
② 非Bin管理直接由系统分配的内存、内存池FPoolInfo、哈希桶PoolHashBucket、FFreeMem分配内存时对分配大小进行PageSize对齐
UPTRINT OsBytes = Align(Bytes, PageSize);
注:OS分配内存的基本单元为页
③ 从FFreeMem上分配Block时对Ptr指针进行内存对齐 注:见AllocateBlockFromPool函数
FFreeMem* ret = Align(Free, Alignment); // Alignment为16
注:对齐到16字节,可提升内存访问效率
Slack内存
新分配的内存池往往不能立即被全部利用,这些未被利用的空闲Block为Slack内存
内存浪费(Waste)
① 非Bin管理直接由系统分配的内存、内存池FPoolInfo、哈希桶PoolHashBucket、FFreeMem分配内存时对分配大小进行PageSize对齐造成的浪费 注:下面以windows平台为例来说明
UPTRINT AlignedSize = Align(Size, 65536); // Size必须对齐到65536的整数倍 如:Size为48时,AlignedSize则为65536,这里会有一个对齐造成的浪费65536-65520=16
UPTRINT AlignedSize = Align(Size, 65536); // Size必须对齐到65536的整数倍 如:Size为50KB时,AlignedSize则为64KB,这里会有一个对齐造成的浪费64KB-50KB=14KB
② 维护分配器的内存池表MemSizeToPoolTable、内存池FPoolInfo、哈希桶(HashBuckets、HashBucketFreeList)等额外产生的内存
③ Block内部的浪费(如大小为[9, 16]的内存块都映射到BlockSize为16的内存池表),这也导致了一定比例的内存浪费 注:这块的浪费DumpPlatformAndAllocatorStats中Mem Waste是没有包含在内的
统计内存分配器
注:下面只统计了从Pool中分配内存的情况,直接从OS哪儿分配的没有统计
Current OS Memory:内存池当前从OS申请的内存
Current Memory(Current Used):已被分配Block块的内存
Current Waste:FFreeMem对齐造成的浪费 + FPoolInfo占用的内存 + HashBucketFreeList占用的内存
Current Slack:Current OS Memory - Current Used - 所有Waste,大概为所有空闲Block内存综合
Num Pools:分配Pool总次数 - 释放Pool总次数
Max Pools:分配Pool总次数
Cur Allocs:分配内存总次数 - 释放内存总次数
Total Allocs:分配内存总次数
Min Req:最小请求分配的内存size
Max Req:最大请求分配的内存size
Mem Used:已分配Blcok的内存占用
Mem Slack:空闲Blcok的内存占用
Mem Waste:对齐造成的浪费及额外数据结构的内存占用
Mem Allocated:操作系统分配的总内存
Used Efficiency = Mem Used / Mem Allocated
Efficiency = (MemAllocated - MemWaste) / MemAllocated
Android(小米10)DumpPlatformAndAllocatorStats统计信息:
[2021.04.06-16.59.06:300][713]LogMemory: Platform Memory Stats for Android [2021.04.06-16.59.06:300][713]LogMemory: Process Physical Memory: 1110.25 MB used, 1363.29 MB peak [2021.04.06-16.59.06:300][713]LogMemory: Process Virtual Memory: 8587.64 MB used, 9031.52 MB peak [2021.04.06-16.59.06:300][713]LogMemory: Physical Memory: 5089.31 MB used, 2534.26 MB free, 7623.57 MB total [2021.04.06-16.59.06:300][713]LogMemory: Virtual Memory: 508.07 MB used, 1539.93 MB free, 2048.00 MB total
[2021.04.06-16.59.06:300][713]LogMemory: PageSize: 4096, BinnedPageSize: 65536, BinnedAllocationGranularity: 4096, AddressLimit: 8589934592 [2021.04.06-16.59.06:313][713]LogMemory: Allocator Stats for binned: [2021.04.06-16.59.06:313][713]LogMemory: Current Memory 369.84 MB used, plus 189.21 MB waste [2021.04.06-16.59.06:313][713]LogMemory: Peak Memory 617.60 MB used, plus 69.60 MB waste [2021.04.06-16.59.06:313][713]LogMemory: Current OS Memory 559.05 MB, peak 687.20 MB [2021.04.06-16.59.06:313][713]LogMemory: Current Waste 136.35 MB, peak 136.35 MB [2021.04.06-16.59.06:313][713]LogMemory: Current Used 369.84 MB, peak 617.60 MB [2021.04.06-16.59.06:313][713]LogMemory: Current Slack 48.84 MB [2021.04.06-16.59.06:313][713]LogMemory: Allocs 2398251 Current / 33517546 Total [2021.04.06-16.59.06:313][713]LogMemory: [2021.04.06-16.59.06:313][713]LogMemory: Block Size Num Pools Max Pools Cur Allocs Total Allocs Min Req Max Req Mem Used Mem Slack Mem Waste Mem Allocated Used Efficiency Efficiency [2021.04.06-16.59.06:314][713]LogMemory: ---------- --------- --------- ---------- ------------ ------- ------- -------- --------- --------- ------------- --------------- ---------- [2021.04.06-16.59.06:314][713]LogMemory: 16 319 319 1254564 9098655 0 16 19602K 814K 0K 20416K 96.01% 100.00% [2021.04.06-16.59.06:314][713]LogMemory: 32 180 184 297963 5143970 32 32 9311K 2209K 0K 11520K 80.82% 100.00% [2021.04.06-16.59.06:314][713]LogMemory: 48 160 173 155296 2936524 48 48 7279K 2959K 2K 10240K 71.08% 99.98% [2021.04.06-16.59.06:314][713]LogMemory: 64 225 246 165122 2124726 64 64 10320K 4080K 0K 14400K 71.67% 100.00% [2021.04.06-16.59.06:314][713]LogMemory: 80 237 244 167572 1042217 80 80 13091K 2074K 3K 15168K 86.31% 99.98% [2021.04.06-16.59.06:314][713]LogMemory: 96 107 117 40625 2633852 96 96 3808K 3034K 6K 6848K 55.61% 99.91% [2021.04.06-16.59.06:314][713]LogMemory: 112 41 45 16854 376864 112 112 1843K 781K 0K 2624K 70.24% 100.00% [2021.04.06-16.59.06:314][713]LogMemory: 128 234 265 98658 1040863 128 128 12332K 2644K 0K 14976K 82.35% 100.00% [2021.04.06-16.59.06:314][713]LogMemory: 160 126 161 39071 1898249 144 160 6104K 1949K 69K 8064K 75.69% 99.14% [2021.04.06-16.59.06:314][713]LogMemory: 192 144 162 36918 1561422 176 192 6922K 2285K 61K 9216K 75.11% 99.34% [2021.04.06-16.59.06:314][713]LogMemory: 224 102 123 14295 1421348 208 224 3127K 3389K 54K 6528K 47.90% 99.17% [2021.04.06-16.59.06:314][713]LogMemory: 256 112 121 14272 703740 240 256 3568K 3600K 21K 7168K 49.78% 99.71% [2021.04.06-16.59.06:314][713]LogMemory: 288 66 97 7906 612219 272 288 2223K 1991K 28K 4224K 52.63% 99.34% [2021.04.06-16.59.06:314][713]LogMemory: 320 52 80 6637 142203 304 320 2074K 1241K 16K 3328K 62.32% 99.52% [2021.04.06-16.59.06:314][713]LogMemory: 384 163 253 19416 493349 336 384 7281K 3111K 317K 10432K 69.79% 96.96% [2021.04.06-16.59.06:314][713]LogMemory: 448 59 63 4990 274828 400 448 2183K 1586K 58K 3776K 57.81% 98.46% [2021.04.06-16.59.06:315][713]LogMemory: 512 96 107 9411 247459 464 512 4705K 1439K 188K 6144K 76.58% 96.94% [2021.04.06-16.59.06:315][713]LogMemory: 576 50 75 2091 204011 528 576 1176K 2003K 30K 3200K 36.75% 99.06% [2021.04.06-16.59.06:315][713]LogMemory: 640 158 266 14026 90267 592 640 8766K 1307K 323K 10112K 86.69% 96.81% [2021.04.06-16.59.06:315][713]LogMemory: 704 51 140 4565 72842 656 704 3138K 123K 49K 3264K 96.14% 98.50% [2021.04.06-16.59.06:315][713]LogMemory: 768 24 67 1135 86505 720 768 851K 679K 41K 1536K 55.40% 97.33% [2021.04.06-16.59.06:315][713]LogMemory: 896 78 258 5449 94438 784 896 4767K 216K 146K 4992K 95.49% 97.08% [2021.04.06-16.59.06:315][713]LogMemory: 1024 107 226 4957 170220 912 1024 4957K 1891K 55K 6848K 72.39% 99.20% [2021.04.06-16.59.06:315][713]LogMemory: 1168 40 127 2189 26749 1040 1168 2496K 59K 113K 2560K 97.50% 95.59% [2021.04.06-16.59.06:315][713]LogMemory: 1360 38 126 1393 46971 1184 1360 1850K 573K 52K 2432K 76.07% 97.86% [2021.04.06-16.59.06:315][713]LogMemory: 1632 40 156 1019 116075 1376 1632 1624K 926K 77K 2560K 63.44% 96.99% [2021.04.06-16.59.06:315][713]LogMemory: 2048 81 111 1756 64451 1648 2048 3512K 1672K 118K 5184K 67.75% 97.72% [2021.04.06-16.59.06:315][713]LogMemory: 2336 65 469 934 84287 2064 2336 2130K 2022K 40K 4160K 51.20% 99.04% [2021.04.06-16.59.06:315][713]LogMemory: 2720 13 34 266 58860 2352 2720 706K 123K 21K 832K 84.86% 97.48% [2021.04.06-16.59.06:315][713]LogMemory: 3264 67 71 762 26556 2736 3264 2428K 1844K 62K 4288K 56.62% 98.55% [2021.04.06-16.59.06:315][713]LogMemory: 4096 203 237 2426 63461 3280 4096 9704K 3288K 213K 12992K 74.69% 98.36% [2021.04.06-16.59.06:315][713]LogMemory: 4672 34 73 407 31359 4112 4672 1856K 316K 81K 2176K 85.29% 96.28% [2021.04.06-16.59.06:316][713]LogMemory: 5456 50 131 521 17582 4688 5456 2775K 422K 59K 3200K 86.72% 98.16% [2021.04.06-16.59.06:316][713]LogMemory: 6544 80 206 798 38214 5472 6544 5099K 14K 305K 5120K 99.59% 94.04% [2021.04.06-16.59.06:316][713]LogMemory: 8192 127 300 980 99687 6560 8192 7840K 288K 712K 8128K 96.46% 91.24% [2021.04.06-16.59.06:316][713]LogMemory: 9360 88 153 605 18449 8208 9360 5530K 101K 110K 5632K 98.19% 98.05% [2021.04.06-16.59.06:316][713]LogMemory: 10912 45 113 264 70108 9376 10912 2813K 65K 12K 2880K 97.67% 99.58% [2021.04.06-16.59.06:316][713]LogMemory: 13104 121 299 601 34523 10928 13104 7690K 53K 328K 7744K 99.30% 95.76% [2021.04.06-16.59.06:316][713]LogMemory: 16384 87 274 338 62461 13120 16384 5408K 160K 74K 5568K 97.13% 98.67% [2021.04.06-16.59.06:316][713]LogMemory: 21840 34 143 98 20740 16400 21840 2090K 86K 53K 2176K 96.05% 97.56% [2021.04.06-16.59.06:316][713]LogMemory: 32768 27 98 53 11996 21856 32752 1696K 32K 197K 1728K 98.15% 88.60% [2021.04.06-16.59.06:316][713]LogMemory: [2021.04.06-16.59.06:316][713]LogMemory: 264384K allocated in pools (with 57449K slack and 4094K waste). Efficiency 98.45% [2021.04.06-16.59.06:317][713]LogMemory: Allocations 2397203 Current / 33363300 Total (in 4131 pools) [2021.04.06-16.59.06:317][713]LogMemory:
IOS(iPhone 11)DumpPlatformAndAllocatorStats统计信息:
[2021.04.10-11.52.31:193][947]LogMemory: Platform Memory Stats for IOS [2021.04.10-11.52.31:193][947]LogMemory: Process Physical Memory: 535.86 MB used, 539.75 MB peak [2021.04.10-11.52.31:193][947]LogMemory: Process Virtual Memory: 400188.69 MB used, 400188.69 MB peak [2021.04.10-11.52.31:193][947]LogMemory: Physical Memory: 535.86 MB used, 1563.34 MB free, 2099.20 MB total [2021.04.10-11.52.31:193][947]LogMemory: Virtual Memory: 2099.20 MB used, 0.00 MB free, 2099.20 MB total
[2021.04.10-11.52.31:193][947]LogMemory: LogMemory: PageSize: 16384, BinnedPageSize: 65536, BinnedAllocationGranularity: 0, AddressLimit: 4294967296 [2021.04.10-11.52.31:194][947]LogMemory: Allocator Stats for binned: [2021.04.10-11.52.31:194][947]LogMemory: Current Memory 156.06 MB used, plus 4.51 MB waste [2021.04.10-11.52.31:194][947]LogMemory: Peak Memory 178.45 MB used, plus 3.13 MB waste [2021.04.10-11.52.31:195][947]LogMemory: Current OS Memory 160.58 MB, peak 181.58 MB [2021.04.10-11.52.31:195][947]LogMemory: Current Waste 2.83 MB, peak 3.33 MB [2021.04.10-11.52.31:195][947]LogMemory: Current Used 156.07 MB, peak 178.45 MB [2021.04.10-11.52.31:195][947]LogMemory: Current Slack 1.68 MB [2021.04.10-11.52.31:195][947]LogMemory: Allocs 2307386 Current / 12279772 Total [2021.04.10-11.52.31:195][947]LogMemory: [2021.04.10-11.52.31:195][947]LogMemory: Block Size Num Pools Max Pools Cur Allocs Total Allocs Min Req Max Req Mem Used Mem Slack Mem Waste Mem Allocated Used Efficiency Efficiency [2021.04.10-11.52.31:195][947]LogMemory: ---------- --------- --------- ---------- ------------ ------- ------- -------- --------- --------- ------------- --------------- ---------- [2021.04.10-11.52.31:195][947]LogMemory: 16 181 181 182906 1948214 0 16 2857K 39K 0K 2896K 98.65% 100.00% [2021.04.10-11.52.31:195][947]LogMemory: 32 0 0 0 0 32 0 0K 0K 0K 0K 100.00% 100.00% [2021.04.10-11.52.31:195][947]LogMemory: 48 0 0 0 0 48 0 0K 0K 0K 0K 100.00% 100.00% [2021.04.10-11.52.31:195][947]LogMemory: 64 0 0 0 0 64 0 0K 0K 0K 0K 100.00% 100.00% [2021.04.10-11.52.31:195][947]LogMemory: 80 0 0 0 0 80 0 0K 0K 0K 0K 100.00% 100.00% [2021.04.10-11.52.31:196][947]LogMemory: 96 0 0 0 0 96 0 0K 0K 0K 0K 100.00% 100.00% [2021.04.10-11.52.31:196][947]LogMemory: 112 0 0 0 0 112 0 0K 0K 0K 0K 100.00% 100.00% [2021.04.10-11.52.31:196][947]LogMemory: 128 0 0 0 0 128 0 0K 0K 0K 0K 100.00% 100.00% [2021.04.10-11.52.31:196][947]LogMemory: 160 0 0 0 0 160 0 0K 0K 0K 0K 100.00% 100.00% [2021.04.10-11.52.31:196][947]LogMemory: 192 0 0 0 0 192 0 0K 0K 0K 0K 100.00% 100.00% [2021.04.10-11.52.31:196][947]LogMemory: 224 0 0 0 0 224 0 0K 0K 0K 0K 100.00% 100.00% [2021.04.10-11.52.31:196][947]LogMemory: 256 0 0 0 0 256 0 0K 0K 0K 0K 100.00% 100.00% [2021.04.10-11.52.31:196][947]LogMemory: 288 141 141 7512 90824 272 288 2112K 109K 55K 2256K 93.62% 97.56% [2021.04.10-11.52.31:196][947]LogMemory: 320 67 67 3295 45435 304 320 1029K 39K 5K 1072K 95.99% 99.53% [2021.04.10-11.52.31:196][947]LogMemory: 384 166 167 6768 104320 336 384 2538K 77K 123K 2656K 95.56% 95.37% [2021.04.10-11.52.31:196][947]LogMemory: 448 100 100 2918 22356 400 448 1276K 299K 36K 1600K 79.75% 97.75% [2021.04.10-11.52.31:196][947]LogMemory: 512 230 231 7209 43762 464 512 3604K 76K 71K 3680K 97.93% 98.07% [2021.04.10-11.52.31:196][947]LogMemory: 576 70 70 1882 71562 528 576 1058K 45K 47K 1120K 94.46% 95.80% [2021.04.10-11.52.31:196][947]LogMemory: 640 178 186 4321 45755 592 640 2700K 82K 82K 2848K 94.80% 97.12% [2021.04.10-11.52.31:197][947]LogMemory: 704 45 49 1002 30304 656 704 688K 24K 8K 720K 95.56% 98.89% [2021.04.10-11.52.31:197][947]LogMemory: 768 18 19 363 5304 720 768 272K 12K 12K 288K 94.44% 95.83% [2021.04.10-11.52.31:197][947]LogMemory: 896 362 382 6381 15204 784 896 5583K 119K 374K 5792K 96.39% 93.54% [2021.04.10-11.52.31:197][947]LogMemory: 1024 123 127 1835 32420 912 1024 1835K 133K 18K 1968K 93.24% 99.09% [2021.04.10-11.52.31:197][947]LogMemory: 1168 51 55 686 5670 1040 1168 782K 33K 31K 816K 95.83% 96.20% [2021.04.10-11.52.31:197][947]LogMemory: 1360 40 41 468 4761 1184 1360 621K 17K 26K 640K 97.03% 95.94% [2021.04.10-11.52.31:197][947]LogMemory: 1632 49 93 463 23859 1376 1632 737K 44K 25K 784K 94.01% 96.81% [2021.04.10-11.52.31:197][947]LogMemory: 2048 304 305 2431 13893 1648 2048 4862K 2K 137K 4864K 99.96% 97.18% [2021.04.10-11.52.31:197][947]LogMemory: 2336 75 258 365 12225 2064 2336 832K 366K 7K 1200K 69.33% 99.42% [2021.04.10-11.52.31:197][947]LogMemory: 2720 74 77 438 6675 2352 2720 1163K 17K 12K 1184K 98.23% 98.99% [2021.04.10-11.52.31:197][947]LogMemory: 3264 267 268 1331 4664 2736 3264 4242K 14K 75K 4272K 99.30% 98.24% [2021.04.10-11.52.31:197][947]LogMemory: 4096 109 140 433 4251 3280 4096 1732K 12K 43K 1744K 99.31% 97.53% [2021.04.10-11.52.31:197][947]LogMemory: 4672 127 128 374 4611 4128 4672 1706K 33K 407K 2032K 83.96% 79.97% [2021.04.10-11.52.31:197][947]LogMemory: 5456 28 36 81 450 4688 5456 431K 17K 19K 448K 96.21% 95.76% [2021.04.10-11.52.31:198][947]LogMemory: 6544 94 134 188 7304 5472 6544 1201K 1K 438K 1504K 79.85% 70.88% [2021.04.10-11.52.31:198][947]LogMemory: 8192 19 29 36 249 6560 8176 288K 16K 22K 304K 94.74% 92.76% [2021.04.10-11.52.31:198][947]LogMemory: 12288 103 106 408 13961 0 12288 4896K 48K 584K 4944K 99.03% 88.19% [2021.04.10-11.52.31:198][947]LogMemory: 24576 16 16 59 4289 0 24576 1416K 120K 11K 1536K 92.19% 99.28% [2021.04.10-11.52.31:198][947]LogMemory: [2021.04.10-11.52.31:198][947]LogMemory: 53168K allocated in pools (with 1794K slack and 2668K waste). Efficiency 94.98% [2021.04.10-11.52.31:198][947]LogMemory: Allocations 234153 Current / 2562322 Total (in 3037 pools) [2021.04.10-11.52.31:198][947]LogMemory:
注: iOS 对小内存分配做了优化,小于等于PLAT_SMALL_BLOCK_POOL_SIZE(256)直接走iOS系统的MALLOC_NANO内存池,效率更好
PC下DumpPlatformAndAllocatorStats统计信息:
[2021.04.12-15.14.12:989][356]LogMemory: Platform Memory Stats for Windows
[2021.04.12-15.14.12:989][356]LogMemory: Process Physical Memory: 2228.61 MB used, 2272.98 MB peak
[2021.04.12-15.14.12:989][356]LogMemory: Process Virtual Memory: 2559.69 MB used, 2625.18 MB peak
[2021.04.12-15.14.12:989][356]LogMemory: Physical Memory: 17139.26 MB used, 15461.84 MB free, 32601.11 MB total
[2021.04.12-15.14.12:990][356]LogMemory: Virtual Memory: 134183992.00 MB used, 33735.41 MB free, 134217728.00 MB total
[2021.04.12-15.14.12:991][356]LogMemory: PageSize: 4096, BinnedPageSize: 65536, BinnedAllocationGranularity: 4096, AddressLimit: 34359738368
[2021.04.12-15.14.12:995][356]LogMemory: Allocator Stats for binned:
[2021.04.12-15.14.12:995][356]LogMemory: Current Memory 1609.61 MB used, plus 25.77 MB waste
[2021.04.12-15.14.12:996][356]LogMemory: Peak Memory 1656.91 MB used, plus -12.10 MB waste
[2021.04.12-15.14.12:996][356]LogMemory: Current OS Memory 1635.38 MB, peak 1644.81 MB
[2021.04.12-15.14.12:996][356]LogMemory: Current Waste 6.75 MB, peak 10.16 MB
[2021.04.12-15.14.12:996][356]LogMemory: Current Used 1609.61 MB, peak 1656.91 MB
[2021.04.12-15.14.12:996][356]LogMemory: Current Slack 9.08 MB
[2021.04.12-15.14.12:996][356]LogMemory: Allocs 8761345 Current / 59260190 Total
[2021.04.12-15.14.12:996][356]LogMemory:
[2021.04.12-15.14.12:996][356]LogMemory: Block Size Num Pools Max Pools Cur Allocs Total Allocs Min Req Max Req Mem Used Mem Slack Mem Waste Mem Allocated Used Efficiency Efficiency
[2021.04.12-15.14.12:996][356]LogMemory: ---------- --------- --------- ---------- ------------ ------- ------- -------- --------- --------- ------------- --------------- ----------
[2021.04.12-15.14.12:996][356]LogMemory: 16 611 611 2501292 26173122 0 16 39082K 22K 0K 39104K 99.94% 100.00%
[2021.04.12-15.14.12:996][356]LogMemory: 32 1053 1053 2155389 6090630 32 32 67355K 37K 0K 67392K 99.95% 100.00%
[2021.04.12-15.14.12:996][356]LogMemory: 48 722 722 984253 3978065 48 48 46136K 61K 11K 46208K 99.84% 99.98%
[2021.04.12-15.14.12:997][356]LogMemory: 64 586 586 599984 10991809 64 64 37499K 5K 0K 37504K 99.99% 100.00%
[2021.04.12-15.14.12:997][356]LogMemory: 80 624 624 510785 1491519 80 80 39905K 22K 9K 39936K 99.92% 99.98%
[2021.04.12-15.14.12:997][356]LogMemory: 96 226 226 153644 1408916 96 96 14404K 46K 14K 14464K 99.59% 99.90%
[2021.04.12-15.14.12:997][356]LogMemory: 112 822 870 472933 1740732 112 112 51727K 869K 12K 52608K 98.33% 99.98%
[2021.04.12-15.14.12:997][356]LogMemory: 128 183 183 93557 647273 128 128 11694K 18K 0K 11712K 99.85% 100.00%
[2021.04.12-15.14.12:997][356]LogMemory: 160 703 703 287123 1290309 144 160 44862K 65K 768K 44992K 99.71% 98.29%
[2021.04.12-15.14.12:997][356]LogMemory: 192 402 402 136848 1151200 176 192 25659K 44K 236K 25728K 99.73% 99.08%
[2021.04.12-15.14.12:997][356]LogMemory: 224 254 254 74116 893207 208 224 16212K 13K 95K 16256K 99.73% 99.42%
[2021.04.12-15.14.12:997][356]LogMemory: 256 333 333 85179 807085 240 256 21294K 18K 141K 21312K 99.92% 99.34%
[2021.04.12-15.14.12:998][356]LogMemory: 288 226 226 51263 521744 272 288 14417K 12K 50K 14464K 99.68% 99.65%
[2021.04.12-15.14.12:998][356]LogMemory: 320 911 911 185745 686048 304 320 58045K 32K 305K 58304K 99.56% 99.48%
[2021.04.12-15.14.12:998][356]LogMemory: 384 598 598 101327 318333 336 384 37997K 126K 721K 38272K 99.28% 98.12%
[2021.04.12-15.14.12:998][356]LogMemory: 448 834 996 66154 212807 400 448 28942K 24330K 1145K 53376K 54.22% 97.85%
[2021.04.12-15.14.12:998][356]LogMemory: 512 529 529 67112 115991 464 512 33556K 300K 236K 33856K 99.11% 99.30%
[2021.04.12-15.14.12:998][356]LogMemory: 576 177 177 19995 71126 528 576 11247K 4K 107K 11328K 99.28% 99.06%
[2021.04.12-15.14.12:998][356]LogMemory: 640 328 328 33376 75495 592 640 20860K 50K 319K 20992K 99.37% 98.48%
[2021.04.12-15.14.12:998][356]LogMemory: 704 138 138 12833 30536 656 704 8822K 2K 55K 8832K 99.89% 99.38%
[2021.04.12-15.14.12:999][356]LogMemory: 768 124 124 10483 24698 720 768 7862K 43K 114K 7936K 99.07% 98.56%
[2021.04.12-15.14.12:999][356]LogMemory: 896 555 555 40491 79794 784 896 35429K 22K 989K 35520K 99.74% 97.22%
[2021.04.12-15.14.12:999][356]LogMemory: 1024 467 467 29734 79974 912 1024 29734K 154K 426K 29888K 99.48% 98.57%
[2021.04.12-15.14.12:999][356]LogMemory: 1168 489 489 27340 36731 1040 1168 31184K 51K 1497K 31296K 99.64% 95.22%
[2021.04.12-15.14.12:999][356]LogMemory: 1360 191 191 9150 36333 1184 1360 12152K 25K 294K 12224K 99.41% 97.59%
[2021.04.12-15.14.12:999][356]LogMemory: 1632 338 338 13518 46607 1376 1632 21544K 4K 647K 21632K 99.59% 97.01%
[2021.04.12-15.14.12:999][356]LogMemory: 2048 196 197 6242 34291 1648 2048 12484K 60K 95K 12544K 99.52% 99.24%
[2021.04.12-15.14.12:999][356]LogMemory: 2336 164 167 4587 39066 2064 2336 10464K 12K 103K 10496K 99.70% 99.02%
[2021.04.12-15.14.12:999][356]LogMemory: 2720 97 97 2308 11909 2352 2720 6130K 54K 75K 6208K 98.74% 98.79%
[2021.04.12-15.14.12:999][356]LogMemory: 3264 147 147 2920 32822 2736 3264 9307K 65K 102K 9408K 98.93% 98.92%
[2021.04.12-15.14.12:999][356]LogMemory: 4096 436 555 5169 25559 3280 4096 20676K 7228K 71K 27904K 74.10% 99.75%
[2021.04.12-15.14.12:999][356]LogMemory: 4672 107 107 1485 8120 4112 4672 6775K 60K 91K 6848K 98.93% 98.67%
[2021.04.12-15.14.12:999][356]LogMemory: 5456 213 214 2437 6684 4688 5456 12984K 635K 71K 13632K 95.25% 99.48%
[2021.04.12-15.14.13:000][356]LogMemory: 6544 112 112 1104 12038 5472 6544 7055K 103K 198K 7168K 98.42% 97.24%
[2021.04.12-15.14.13:000][356]LogMemory: 8192 462 462 3603 11076 6560 8192 28824K 744K 558K 29568K 97.48% 98.11%
[2021.04.12-15.14.13:000][356]LogMemory: 9360 240 244 1577 10352 8208 9360 14414K 943K 29K 15360K 93.84% 99.81%
[2021.04.12-15.14.13:000][356]LogMemory: 10912 85 87 480 6702 9376 10912 5115K 320K 16K 5440K 94.03% 99.71%
[2021.04.12-15.14.13:000][356]LogMemory: 13104 272 275 1294 6295 10960 13104 16559K 845K 90K 17408K 95.12% 99.48%
[2021.04.12-15.14.13:000][356]LogMemory: 16384 217 218 835 12553 13120 16384 13360K 528K 50K 13888K 96.20% 99.64%
[2021.04.12-15.14.13:000][356]LogMemory: 21840 188 192 549 6209 16400 21840 11709K 321K 102K 12032K 97.32% 99.15%
[2021.04.12-15.14.13:000][356]LogMemory: 32768 71 74 142 860 21904 32752 4544K 0K 308K 4544K 100.00% 93.22%
[2021.04.12-15.14.13:000][356]LogMemory: 49152 449 453 1749 14113 0 49152 83952K 2256K 15422K 86208K 97.38% 82.11%
[2021.04.12-15.14.13:000][356]LogMemory: 98304 75 75 296 4172 0 98304 28416K 384K 126K 28800K 98.67% 99.56%
[2021.04.12-15.14.13:000][356]LogMemory:
[2021.04.12-15.14.13:000][356]LogMemory: 1102592K allocated in pools (with 40933K slack and 25698K waste). Efficiency 97.67%
[2021.04.12-15.14.13:000][356]LogMemory: Allocations 8760401 Current / 59242905 Total (in 15955 pools)
[2021.04.12-15.14.13:000][356]LogMemory:
DumpPlatformAndAllocatorStats中的逻辑如下:
void USGGameInstanceConsoleCommandComponent::DumpPlatformAndAllocatorStats() { if (GLog) { FPlatformMemory::DumpPlatformAndAllocatorStats(*GLog); } } void FGenericPlatformMemory::DumpPlatformAndAllocatorStats( class FOutputDevice& Ar ) { FPlatformMemory::DumpStats( Ar ); GMalloc->DumpAllocatorStats( Ar ); }