zoukankan      html  css  js  c++  java
  • 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

    申请内存时:

    释放内存时:

  • 相关阅读:
    Centos 7.3 配置Xmanager XDMCP
    xstart使用方法
    Linux下安装xwindow图形界面
    使用Xftp连接Centos 6.6服务器详细图文教程
    linux远程管理器
    xftp的使用教程
    CentOS 7 关闭图形界面
    Java反射机制
    java反射的性能问题
    Java 虚拟机面试题全面解析(干货)
  • 原文地址:https://www.cnblogs.com/kekec/p/12012537.html
Copyright © 2011-2022 走看看