ue4內存管理


ue4內存管理

自定義內存管理

 

 

ue4的內存管理主要是通過FMalloc類型的GMalloc這一結構來完成特定的需求,這是一個虛基類,它定義了malloc,realloc,free等一系列常用的內存管理操作。其中,Malloc的兩個參數分別是分配內存的大小和對應的對齊量,默認對齊量為0。

/** The global memory allocator's interface. */
class CORE_API FMalloc  : 
 public FUseSystemMallocForNew,
 public FExec
{
public:
 virtual void* Malloc( SIZE_T Count, uint32 Alignment=DEFAULT_ALIGNMENT ) = 0;
 virtual void* TryMalloc( SIZE_T Count, uint32 Alignment=DEFAULT_ALIGNMENT );
 virtual void* Realloc( void* Original, SIZE_T Count, uint32 Alignment=DEFAULT_ALIGNMENT ) = 0;
 virtual void* TryRealloc(void* Original, SIZE_T Count, uint32 Alignment=DEFAULT_ALIGNMENT);
 virtual void Free( void* Original ) = 0;
  
 // ...
};

FMalloc有許多不同的實現,如FMallocBinned,FMallocBinned2等,可以在HAL文件夾下找到相關的頭文件和定義,如下:

 

 

內部通過枚舉量來確定對應使用的Allocator:

 /** 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
 };

對於不同平台而言,都有自己對應的平台內存管理類,它們繼承自FGenericPlatformMemory,封裝了平台相關的內存操作。具體而言,包含FAndroidPlatformMemory,FApplePlatformMemory,FIOSPlatformMemory,FWindowsPlatformMemory等。

通過調用PlatformMemory的BaseAllocator函數,我們取得平台對應的FMalloc類型,基類默認返回默認的C allocator,而不同平台會有自己特殊的實現。

在PlatformMemory的基礎上,為了方便調用,ue4又封裝了FMemory類,定義通用內存操作,如在申請內存時,會調用FMemory::Malloc,FMemory內部又會繼續調用GMalloc->Malloc。如下為節選代碼:

struct CORE_API FMemory
{
 /** @name Memory functions (wrapper for FPlatformMemory) */

 static FORCEINLINE void* Memmove( void* Dest, const void* Src, SIZE_T Count )
 {
  return FPlatformMemory::Memmove( Dest, Src, Count );
 }

 static FORCEINLINE int32 Memcmp( const void* Buf1, const void* Buf2, SIZE_T Count )
 {
  return FPlatformMemory::Memcmp( Buf1, Buf2, Count );
 }

 // ...
 static void* Malloc(SIZE_T Count, uint32 Alignment = DEFAULT_ALIGNMENT);
 static void* Realloc(void* Original, SIZE_T Count, uint32 Alignment = DEFAULT_ALIGNMENT);
 static void Free(void* Original);
 static SIZE_T GetAllocSize(void* Original);

    // ...
};

為了在調用new/delete能夠調用ue4的自定義函數,ue4內部替換了operator new。這一替換是通過IMPLEMENT_MODULE宏引入的:

 

 

IMPLEMENT_MODULE通過定義REPLACEMENT_OPERATOR_NEW_AND_DELETE宏實現替換,如下圖所示,operator new/delete內實際調用被替換為FMemory的相關函數。

 

 

FMallocBinned

我們以FMallocBinned為例介紹ue4中通用內存的分配。

基本介紹

(1) 空閑內存如何管理?

FMallocBinned使用freelist機制管理空閑內存。每個空閑塊的信息記錄在FFreeMem結構中,顯式存儲。

(2)不同大小內存如何分配?

FMallocBinned使用內存池機制,內部包含POOL_COUNT(42)個內存池和2個擴展的頁內存池;其中每個內存池的信息由FPoolInfo結構體維護,記錄了當前FreeMem內存塊指針等,而特定大小的所有內存池由FPoolTable維護;內存池內包含了內存塊的雙向鏈表。

(3)如何快速根據分配元素大小找到對應的內存池?

為了快速查詢當前分配內存大小應該對應使用哪個內存池,有兩種辦法,一種是二分搜索O(logN),另一種是打表(O1),考慮到可分配內存數量並不大,MallocBinned選擇了打表的方式,將信息記錄在MemSizeToPoolTable。

(4)如何快速刪除已分配內存?

為了能夠在釋放的時候以O(1)時間找到對應內存池,FMallocBinned維護了PoolHashBucket結構用於跟蹤內存分配的記錄。它組織為雙向鏈表形式,存儲了對應內存塊和鍵值。

內存池

● 多個小對象內存池(內存池大小均為PageSize,但存儲的數據量不一樣)。數據塊大小設定如下:

 

 

● 兩個額外的頁內存池,管理大於一個頁的內存池,大小為3*PageSize和6*PageSize

● 操作系統的內存池

分配策略

分配內存的函數為void* FMallocBinned::Malloc(SIZE_T Size, uint32 Alignment)。

其中第一個參數為需要分配的內存的大小,第二個參數為對齊的內存數。

如果用戶未指定對齊的內存大小,MallocBinned內部會默認對齊於16字節,如果指定了大於16字節的對齊內存大小,則對齊於用戶指定的對齊大小。根據對齊量,計算出最終實際分配的內存大小。

MallocBinned內部對於不同的內存大小有三種不同的處理:

(1) 分配小塊內存(0,PAGE_SIZE_LIMIT/2)

根據分配大小從MemSizeToPoolTable中獲取對應內存池,並從內存池的當前空閑位置讀取一塊內存,並移動當前內存指針。如果移動后的內存指針指向的內存塊已經使用,則將指針移動到FreeMem鏈表的下一個元素;如果當前內存池已滿,將該內存池移除,並鏈接到耗盡的內存池。

如果當前內存池已經用盡,下次內存分配時,檢測到內存池用盡,會從系統重新申請一塊對應大小的內存池。

(2) 分配大塊內存 [PAGE_SIZE_LIMIT/2, PAGE_SIZE_LIMIT*3/4]∪(PageSize,PageSize + PAGE_SIZE_LIMIT/2)

需要從額外的頁內存池分配,分配方式和(1)一樣。

(3) 分配超大內存

從系統內存池中分配。

Allocator

對於ue4中的容器而言,它的模板有兩個參數,第一個是元素類型,第二個就是對應的分配器(Allocator):

template<typename InElementType, typename InAllocator>
class TArray
{
   // ...
};

如下圖,容器一般都指定了自己默認的分配器:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM