UE4内存分配器概述

UE4支持多种内存分配器:

/** Which allocator is being used */
enum EMemoryAllocatorToUse
{
    Ansi, // Default C allocator
    Stomp, // Allocator to check for memory stomping
    TBB, // Thread Building Blocks malloc
    Jemalloc, // Linux/FreeBSD malloc
    Binned, // Older binned malloc
    Binned2, // Newer binned malloc
    Binned3, // Newer VM-based binned malloc, 64 bit only
    Platform, // Custom platform specific allocator
    Mimalloc, // mimalloc
};

具体包括如下几类:

Ansi内存分配器(标准C):直接调用mallocfreerealloc函数

TBB(Thread Building Blocks)内存分配器:Intel 提供的第三方库的一个可伸缩内存分配器(Scalable Memory Allocator)

Jemalloc内存分配器(Linux / FreeBSD):适合多线程下的内存分配管理 http://www.canonware.com/jemalloc/

Stomp:用于查非法内存操作(如:内存越界,野指针)的管理方式,目前只支持windows、mac、unix等pc平台。带命令行参数-stompmalloc来启用该分配器

Mimallochttps://github.com/microsoft/mimalloc

UE4内置的内存分配器

Binned(第一代箱式内存分配器)

Binned2(第二代箱式内存分配器)

Binned3(第三代箱式内存分配器,仅支持64bits

对于不同平台而言,都有自己对应的平台内存管理类,它们继承自FGenericPlatformMemory,封装了平台相关的内存操作。

具体而言,包含FAndroidPlatformMemoryFApplePlatformMemoryFIOSPlatformMemoryFWindowsPlatformMemoryFLinuxPlatformMemoryFUnixPlatformMemoryFMacPlatformMemory等。

通过调用FPlatformMemoryBaseAllocator函数,我们取得平台对应的FMalloc类型,基类默认返回默认的Ansi内存分配器(标准C),而不同平台会有自己特殊的实现。

如果想知道当前平台使用了哪种分配器,可以查看对应的PlatformMemory文件。例如WindowsPlatformMemory.cpp文件,找到BaseAllocator函数,会发现Windows平台默认使用了TBB分配器。

FMemory::Malloc申请内存时,会先判断FMalloc* GMalloc(全局的内存分配器)是否已创建,如果没有创建,void FMemory::GCreateMalloc() --》int FMemory_GCreateMalloc_ThreadUnsafe() --》FMallocFPlatformMemory::BaseAllocator()来初始化GMalloc

FMemory是一个静态工具类,对FMalloc* GMalloc进行了封装,具体逻辑详见:

UnrealEngineEngineSourceRuntimeCorePublicHALUnrealMemory.h

UnrealEngineEngineSourceRuntimeCorePublicHALFMemory.inl

UnrealEngineEngineSourceRuntimeCorePrivateHALUnrealMemory.cpp

  Ansi TBB Jemalloc Binned Binned2 Binned3 Mimalloc Stomp
Android 支持     支持 默认 支持(64bits)    
IOS 支持     默认        
Windows 支持 默认   支持 支持 支持(64bits) 支持 支持
Linux 支持   支持 支持 默认     支持
Mac

支持

默认   支持 支持     支持
HoloLens

支持

        默认    

Android下可在/storage/emulated/0/UE4Game目录中放置flag文件来动态启用使用哪种内存分配器:

FMalloc* FAndroidPlatformMemory::BaseAllocator()
{
    // ... ...
    
    if (access("/storage/emulated/0/UE4Game/binned3.flag", 0) == 0)
    {
        return new FMallocBinned3();
    }
    
    // ... ...
}

注1:FUseSystemMallocForNew实现newnew[]、deletedelete[]操作符,主要是为了防止new FMalloc对象时调用到全局的newnew[]、deletedelete[],导致死循环

注2:在游戏线程中,可显示调用Trim函数来释放未被使用的内存页。另外,在gc mark时会调用该函数。  -- FMallocBinned没有实现该函数

注3:FMalloc逻辑详见:UnrealEngineEngineSourceRuntimeCorePublicHALMemoryBase.hUnrealEngineEngineSourceRuntimeCorePrivateHALMemoryBase.cpp

游戏启动时,会打印出当前所用的内存分配器(Alloctator)  详见:void FApp::PrintStartupLogMessages()函数

LogInit: WinSock: version 1.1 (2.2), MaxSocks=32767, MaxUdp=65467
LogOnline: OSS: TryLoadSubsystemAndSetDefault: Loaded subsystem for module [NULL]
LogInit: Build: ++UE4+Release-4.26-CL-0
LogInit: Engine Version: 4.26.1-0+++UE4+Release-4.26
LogInit: Compatible Engine Version: 4.26.0-0+++UE4+Release-4.26
LogInit: Net CL: 0
LogInit: OS: Windows 10 (Release 1903) (), CPU: Intel(R) Core(TM) i9-9900 CPU @ 3.10GHz, GPU: NVIDIA GeForce RTX 2080
LogInit: Compiled (64-bit): Mar 25 2021 19:11:23
LogInit: Compiled with Visual C++: 19.27.29111.00
LogInit: Build Configuration: Debug
LogInit: Branch Name: ++UE4+Release-4.26
LogInit: Command Line:  ThirdPersonExampleMap -messaging -Windowed ResX=800 ResY=600 -game -skipcompile -debug
LogInit: Base Directory: G:/svn/UnrealEngine/Engine/Binaries/Win64/
LogInit: Allocator: TBB
LogInit: Installed Engine Build: 0

内存体系结构

重写全局new、delete

详见:UnrealEngineEngineSourceRuntimeCorePublicModulesBoilerplateModuleBoilerplate.h

OPERATOR_NEW_MSVC_PRAGMA void* operator new  ( size_t Size                        ) OPERATOR_NEW_THROW_SPEC      { return FMemory::Malloc( Size ); } // 正常版本new
OPERATOR_NEW_MSVC_PRAGMA void* operator new[]( size_t Size                        ) OPERATOR_NEW_THROW_SPEC      { return FMemory::Malloc( Size ); } // 正常版本new[]
OPERATOR_NEW_MSVC_PRAGMA void* operator new  ( size_t Size, const std::nothrow_t& ) OPERATOR_NEW_NOTHROW_SPEC    { return FMemory::Malloc( Size ); } // 兼容早期版本的new,内存分配失败不会抛出异常
OPERATOR_NEW_MSVC_PRAGMA void* operator new[]( size_t Size, const std::nothrow_t& ) OPERATOR_NEW_NOTHROW_SPEC    { return FMemory::Malloc( Size ); } // 兼容早期版本的new[],内存分配失败不会抛出异常
void operator delete  ( void* Ptr )                                                 OPERATOR_DELETE_THROW_SPEC   { FMemory::Free( Ptr ); } // 正常版本delete
void operator delete[]( void* Ptr )                                                 OPERATOR_DELETE_THROW_SPEC   { FMemory::Free( Ptr ); } // 正常版本delete[]
void operator delete  ( void* Ptr, const std::nothrow_t& )                          OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } // 兼容早期版本的delete,内存释放失败不会抛出异常
void operator delete[]( void* Ptr, const std::nothrow_t& )                          OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } // 兼容早期版本的delete,内存释放失败不会抛出异常
void operator delete  ( void* Ptr, size_t Size )                                    OPERATOR_DELETE_THROW_SPEC   { FMemory::Free( Ptr ); } // 正常版本delete(带size)
void operator delete[]( void* Ptr, size_t Size )                                    OPERATOR_DELETE_THROW_SPEC   { FMemory::Free( Ptr ); } // 正常版本delete[](带size)
void operator delete  ( void* Ptr, size_t Size, const std::nothrow_t& )             OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } // 兼容早期版本的delete(带size),内存释放失败不会抛出异常
void operator delete[]( void* Ptr, size_t Size, const std::nothrow_t& )             OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } // 兼容早期版本的delete[](带size),内存释放失败不会抛出异常

裸指针  new / delete

{
    int32* p1 = new int32;
    delete p1;
}

申请内存时:

释放内存时:

裸指针 new [] / delete []

{
    float* p2 = new float[5];
    delete[] p2;
}

申请内存时:

释放内存时:

智能指针 new / delete

{
    TUniquePtr<double> up1(new double(10.8));
}

申请内存时:

释放内存时:

智能指针 new[] / delete[]

{
    TUniquePtr<char[]> up2 = MakeUnique<char[]>(25);
}

申请内存时:

释放内存时:

 

UE4容器

TArray缺省内存分配器为FDefaultAllocator  --》TSizedDefaultAllocator<32> --》TSizedHeapAllocator

TMapTSet缺省内存分配器为FDefaultSetAllocator --》TSetAllocator  --》TSparseArrayAllocator<FDefaultAllocator,FDefaultBitArrayAllocator>

详见:UnrealEngineEngineSourceRuntimeCorePublicContainersContainersFwd.h

template<int IndexSize> class TSizedDefaultAllocator;
using FDefaultAllocator = TSizedDefaultAllocator<32>;
using FDefaultAllocator64 = TSizedDefaultAllocator<64>;
class FDefaultSetAllocator;


template<typename T, typename Allocator = FDefaultAllocator> class TArray;

template<typename KeyType, typename ValueType, bool bInAllowDuplicateKeys> struct TDefaultMapHashableKeyFuncs;
template<typename KeyType, typename ValueType, typename SetAllocator = FDefaultSetAllocator, typename KeyFuncs = TDefaultMapHashableKeyFuncs<KeyType, ValueType, false> > class TMap;
template<typename KeyType, typename ValueType, typename SetAllocator = FDefaultSetAllocator, typename KeyFuncs = TDefaultMapHashableKeyFuncs<KeyType, ValueType, true > > class TMultiMap;
template <typename T = void > struct TLess;
template <typename> struct TTypeTraits;
template<typename KeyType, typename ValueType, typename ArrayAllocator = FDefaultAllocator, typename SortPredicate = TLess<typename TTypeTraits<KeyType>::ConstPointerType> > class TSortedMap;

template<typename ElementType,bool bInAllowDuplicateKeys = false> struct DefaultKeyFuncs;
template<typename InElementType, typename KeyFuncs = DefaultKeyFuncs<InElementType>, typename Allocator = FDefaultSetAllocator> class TSet;

FString

{
    FString str1 = TEXT("Hello World!");// 字符串长度为12,Realloc中NewSize值:(字符串长度+1) * 2 = 26
} // 离开作用域,调用Free释放内存

注:FString中包含一个TArray<TCHAR> Data的成员变量

申请内存时:

释放内存时:

TArray

{
    TArray<int32> arr1;
    arr1.Add(12); // Add第一个元素时,ArrayMax为4,Realloc中NewSize值:16  注:TArray申请的空间要多一些,不会Add一个,申请一个的内存,当空间不够时会按照一定的算法来扩容  详见:DefaultCalculateSlackGrow函数
    arr1.Add(13);
} // 离开作用域,调用Free释放内存

申请内存时:

释放内存时:

TSet

{
    TSet<double> set1;
    set1.Add(1.2); // Add第一个元素时,申请内存,Realloc中NewSize值:64  注:TSet申请的空间要多一些,不会Add一个,申请一个的内存,当空间不够时会按照一定的算法来扩容
    set1.Add(2.5);
} // 离开作用域,调用Realloc释放内存(NewSize传入0)

申请内存时:

释放内存时:

TMap

{
    TMap<int32, int32> map1;
    map1.Add(1, 18);// Add第一个元素时,申请内存,Realloc中NewSize值:64   注:TMap申请的空间要多一些,不会Add一个,申请一个的内存,当空间不够时会按照一定的算法来扩容
    map1.Add(2, 28);
    map1.Add(3, 38);
} // 离开作用域,调用Realloc释放内存(NewSize传入0)

申请内存时:

释放内存时:

 

UObject

FString MyBPObjectPath = TEXT("/Game/ThirdPersonCPP/Blueprints/MyBlueprintObject.MyBlueprintObject_C");
UClass* MyBPObjectClass = LoadClass<UObject>(nullptr, *MyBPObjectPath); // MyBPObjectClass为UBlueprintGeneratedClass*类型

UMyBPObject* BPObj1 = NewObject<UMyBPObject>(this, MyBPObjectClass);  // sizeof(UMyBPObject)为48  注:考虑到内存对齐,AllocateUObject时传入的Size为64
CollectGarbage(RF_NoFlags); // 全量阻塞gc

申请内存时:

释放内存时:

原文地址:https://www.cnblogs.com/kekec/p/12012537.html