zoukankan      html  css  js  c++  java
  • 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的内存池链表
        FPoolInfo*            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个数
    };

    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平台为例)

    内存对齐     // 详见:UnrealEngineEngineSourceRuntimeCorePublicTemplatesAlignmentTemplates.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:

    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)

  • 相关阅读:
    FDTD之嵌套扫描
    FDTD之参数扫描与优化
    FDTD之mesh refinement option
    慢光相关知识
    常用单位和公式
    光学知识
    vivo提前批前端笔试题目——辛运员工抽奖
    新版vscode(v1.57)必备配置项——文件标签栏换行展示;Edge集成调试
    无线网络技术实验七——隐藏节点和暴露节点仿真实验
    无线网络技术实验十——卫星网络系统仿真实验
  • 原文地址:https://www.cnblogs.com/kekec/p/14628985.html
Copyright © 2011-2022 走看看