可可西

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; // 内存页大小 // ... ... };

 

FMallocBinnedAndroid平台)的各成员变量数值如下:

注:BinnedSizeLimit != PageSize / 2,因此2个扩展的内存池PagePoolTable的BlockSize为0,相当于被弃用

 

FMallocBinnedIOS平台)的各成员变量数值如下:

 

FMallocBinnedWindows平台)的各成员变量数值如下:

  

注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));
}

 

① MallocReallocQuantizeSize函数中对Size进行内存对齐 

Alignment = FMath::Max<uint32>(Alignment, Private::DEFAULT_BINNED_ALLOCATOR_ALIGNMENT);  // Alignment为16
Size = Align(Size, Alignment);

注:由于sizeof(FFreeMem)为16,分配的Size不能比该值小,否则连FFreeMem都放不下 

 

② 非Bin管理直接由系统分配的内存、内存池FPoolInfo、哈希桶PoolHashBucketFFreeMem分配内存时对分配大小进行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、哈希桶PoolHashBucketFFreeMem分配内存时对分配大小进行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(小米10DumpPlatformAndAllocatorStats统计信息:

[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 11DumpPlatformAndAllocatorStats统计信息:

[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 );
}

 

参考

[引擎开发] 深入C++内存管理

UE4源码剖析:MallocBinned(上)

剖析虚幻渲染体系(01)- 综述和基础

UE4内存管理(1)

posted on 2021-04-19 23:59  可可西  阅读(1752)  评论(1编辑  收藏  举报

导航