CD Code Digest Hub /GTA V · v2

内存:堆拓扑、分配器与游戏池

59 分钟阅读 13 个章节 1 张图

本页描述 GTAV 进程的内存布局:main() 之前由 InitGameHeap() 一次性建立的各平台堆(游戏虚拟堆、资源堆、调试堆、回放堆、Flex/Hemlis/Chunky/Pool/Recorder 等专用堆)、把它们组织成 MEMTYPE_* 槽位的 sysMemMultiAllocator、主要分配器类型(simple + small allocator、buddy/grow buddy/sparse、console 虚拟分配器)、游戏侧对象池(fwPool 的使用方式与 gameconfig.xml 池大小配置),以及内存不足时的处理路径。

fwPool 的内部实现(空闲链表、标志字节、spillover)由 ../04-rage-framework/framework-systems.md 负责;sysIpc、诊断与内存追踪工具见 ../02-rage-core/system-and-diagnostics.md;资源(pgBase/datResource)在资源堆中的放置与重定位见 ../02-rage-core/paging-and-resources.md;流式系统如何使用资源堆见 ../04-rage-framework/streaming-engine.md。

背景与相关页面#

相关源码文件#

文件作用
src/dev_ng/game/Core/main.cpp游戏定义的堆尺寸宏(KB)
src/dev_ng/rage/base/src/system/main.hInitGameHeap():各平台堆的创建顺序、sysMemMultiAllocator 组装、全局 operator new/delete
src/dev_ng/rage/base/src/system/memory.h/.cppeMemoryType、FRAG_HEAP_SIZE、ENABLE_DEBUG_HEAP、sysMemStart*/End* 分配器切换、平台底层分配函数
src/dev_ng/rage/base/src/system/memmanager.hsysMemManager:专用堆(Flex/Replay/Chunky/Pool/Hemlis/Recorder/Frag)的注册表
src/dev_ng/rage/base/src/system/simpleallocator.h/.cpp、smallocator.h主堆分配器与小对象分配器
src/dev_ng/rage/base/src/system/multiallocator.h、buddyallocator.h/.cpp、buddyallocator_config.h、virtualallocator.h、sparseallocator.h其余分配器
src/dev_ng/rage/framework/src/fwsys/config.h/.cpp/.pscfwConfigManager:从配置文件覆盖池大小
src/dev_ng/game/game/config.h/.cppCGameConfig(游戏配置,含线程与各子系统配置)
src/dev_ng/rage/framework/src/fwtl/Pool.h/.cppFW_INSTANTIATE_*_POOL 宏与池存储分配
src/dev_ng/game/scene/scene.cpp、Peds/PedFactory.cpp、Vehicles/VehicleFactory.cpp游戏主要对象池的初始化点
src/dev_ng/game/system/XMemWrapper.*、MemoryCheck.*Xenon XMem 钩子;安全相关检查结构(仅提及)

1. 总览:InitGameHeap 与 MEMTYPE 槽位#

eMemoryType 定义了逻辑堆槽位:MEMTYPE_GAME_VIRTUAL、MEMTYPE_RESOURCE_VIRTUAL、MEMTYPE_GAME_PHYSICAL、MEMTYPE_RESOURCE_PHYSICAL、(PS3/Xenon 的 MEMTYPE_HEADER_VIRTUAL)、MEMTYPE_DEBUG_VIRTUAL(无调试堆时为游戏虚拟堆的别名)src/dev_ng/rage/base/src/system/memory.h:273-286。InitGameHeap() 为每个平台创建具体分配器,按槽位顺序 AddAllocator 到一个静态 sysMemMultiAllocator(最多 8 个子堆 src/dev_ng/rage/base/src/system/multiallocator.h:62),然后把它设为当前线程的 Current/Master/Container 分配器 src/dev_ng/rage/base/src/system/main.h:2016-2019。“不属于任何槽位”的专用堆则登记到单例 sysMemManager(MEMMANAGER)src/dev_ng/rage/base/src/system/memmanager.h:60-138。

图中证据:槽位枚举 src/dev_ng/rage/base/src/system/memory.h:273-284;各平台 AddAllocator 顺序 src/dev_ng/rage/base/src/system/main.h:1509-1513、src/dev_ng/rage/base/src/system/main.h:1632-1636、src/dev_ng/rage/base/src/system/main.h:1887-1912、src/dev_ng/rage/base/src/system/main.h:1978、src/dev_ng/rage/base/src/system/main.h:2002-2006;sysMemManager 成员 src/dev_ng/rage/base/src/system/memmanager.h:109-128;operator new 转发到 GetCurrent() src/dev_ng/rage/base/src/system/main.h:2107。

2. 堆尺寸宏#

game/Core/main.cpp 在包含 system/main.h 前定义下列宏(单位 KB;main.h 为未定义者提供默认值)。64 位平台:

宏PCDurango (XB1)Orbis (PS4)证据
SIMPLE_HEAP_SIZE 游戏虚拟堆474 MB;USE_SPARSE_MEMORY(PC 64 位 __DEV)时 620 MB596 MB596 MBsrc/dev_ng/game/Core/main.cpp:35-45
SIMPLE_PHYSICAL_SIZE 资源(物理)堆256 MB(USE_SPARSE_MEMORY && !FREE_PHYSICAL_RESOURCES 时 8 GB,但 PC 上 FREE_PHYSICAL_RESOURCES 为 1)2482 MB2482 MBsrc/dev_ng/game/Core/main.cpp:73-81、src/dev_ng/rage/base/src/system/buddyallocator_config.h:19-20
SIMPLE_VIRTUAL_SIZE 资源虚拟堆1024 MB00src/dev_ng/game/Core/main.cpp:63-71
POOL_HEAP_SIZE24 MB(仅主机使用)24 MB24 MBsrc/dev_ng/game/Core/main.cpp:31-33
FLEX_HEAP_SIZE––22 MBsrc/dev_ng/game/Core/main.cpp:47-49
HEMLIS_HEAP_SIZE–17 MB(ENABLE_DEBUG_HEAP)/ 220 MB同左src/dev_ng/game/Core/main.cpp:51-57
RECORDER_HEAP_SIZE20 MB(ENABLE_DEBUG_HEAP)同左同左src/dev_ng/game/Core/main.cpp:59-61
DEBUG_HEAP_SIZE220 MB(Xenon/PS3 为 60 MB)220 MB220 MBsrc/dev_ng/game/Core/main.cpp:189-191
REPLAY_HEAP_SIZE(字节)196 MB196 MB196 MBsrc/dev_ng/game/Core/main.cpp:194-196
FRAG_HEAP_SIZE(字节,memory.h)8 MB8 MB8 MBsrc/dev_ng/rage/base/src/system/memory.h:41-50
PGKNOWNREFPOOLSIZE(条目)0x88000x88000x8800src/dev_ng/game/Core/main.cpp:25

注意点:

  • HEMLIS_HEAP_SIZE 的 220 MB 分支只在 ENABLE_DEBUG_HEAP == 0(即 final)时生效,而 Hemlis 堆本身只在 !__FINAL 时创建 src/dev_ng/rage/base/src/system/main.h:1432-1437、src/dev_ng/rage/base/src/system/memory.h:262。这表明实际被创建的 Hemlis 堆总是 17 MB,220 MB 分支是死配置(推断)。
  • FRAG_HEAP_SIZE 在主机与 PC 上的值都是 8 << 20,但行尾注释写着“40 M”,注释与代码不一致 src/dev_ng/rage/base/src/system/memory.h:46-49。
  • Durango 有编译期断言要求 SIMPLE_HEAP_SIZE/DEBUG_HEAP_SIZE 为 4 MB 倍数、REPLAY_HEAP_SIZE 为 4 MB 倍数、FRAG_HEAP_SIZE 为 4 MB 倍数,注释称修改会因 TLB 缺失损失 1+ ms/帧 src/dev_ng/game/Core/main.cpp:198-204、src/dev_ng/rage/base/src/system/memory.h:52-56。
  • Xenon 与 PS3 的宏仍保留(Xenon 按 Bank/Profile/Final 区分游戏堆 92.5–101.5 MB、资源堆约 266–275 MB;PS3 以 GAME_VIRTUAL/VRAM_SIZE 推导),属于遗留路径 src/dev_ng/game/Core/main.cpp:96-186。

3. 各平台堆拓扑#

3.1 PC#

src/dev_ng/rage/base/src/system/main.h:1750-1913

顺序堆分配器大小/备注
1调试堆尺寸–DEBUG_HEAP_SIZE,命令行文本中出现 @debugMemory=<MB> 时覆盖,≥ 2 GB 则 Quitf src/dev_ng/rage/base/src/system/main.h:823-838
2游戏虚拟堆 → 槽 0sysMemSimpleAllocator(HEAP_MAIN);非 final 且命令行含 @sparsegameheap 时改用 sysMemSparseAllocatorSIMPLE_HEAP_SIZE - FRAG_HEAP_SIZE src/dev_ng/rage/base/src/system/main.h:1756、src/dev_ng/rage/base/src/system/main.h:1763-1778
3碎片缓存内存sysMemPhysicalAllocate(FRAG_HEAP_SIZE) → SetFragMemory8 MB src/dev_ng/rage/base/src/system/main.h:1832-1837
4资源物理堆 → 槽 2、3sysMemGrowBuddyAllocator(g_rscPhysicalLeafSize, 256 MB);稀疏模式用 sysMemSparseBuddyAllocator叶大小 8 KB src/dev_ng/rage/base/src/system/main.h:1846-1862、src/dev_ng/rage/base/src/data/resourceheader.h:31-44
5资源虚拟堆 → 槽 1sysMemGrowBuddyAllocator(g_rscVirtualLeafSize, 1 GB);稀疏模式用 sysMemSparseBuddyAllocatorsrc/dev_ng/rage/base/src/system/main.h:1890-1911
6回放堆sysMemVirtualAllocate + sysMemSimpleAllocator(HEAP_REPLAY),不 Quit196 MB;非 final @useDecreasedReplayMemory 减 24 MB src/dev_ng/rage/base/src/system/main.h:1919-1941
7调试堆 → 槽 4sysMemDebugAllocate + sysMemSimpleAllocator(HEAP_DEBUG);无额外内存时槽 4 指向游戏虚拟堆其中再切出 20 MB Recorder 堆 src/dev_ng/rage/base/src/system/main.h:1946-2007

PC 资源堆是可增长的:sysMemGrowBuddyAllocator 构造时先建一个子堆;分配失败时 GrowHeap() 以 min(256 MB, maxSize) 为块大小通过 sysMemVirtualAllocate 再申请一个 buddy 子堆(失败则减半,最小 16 MB),最多 32 个子堆;再失败则把 m_Size 收缩为当前可用+已用并返回 NULL src/dev_ng/rage/base/src/system/buddyallocator.cpp:442-516、src/dev_ng/rage/base/src/system/buddyallocator.cpp:534-579、src/dev_ng/rage/base/src/system/buddyallocator.h:198-200。也就是说构造参数 maxSize(256 MB / 1 GB)实际上限制的是每个子堆的大小,总量上限由 32 个子堆与系统可用虚拟内存决定。PC 的 sysMemVirtualAllocate 用 VirtualAlloc 并在末尾加 4 KB 不可访问的保护页 src/dev_ng/rage/base/src/system/memory.cpp:653-672。

3.2 PS4(Orbis)#

src/dev_ng/rage/base/src/system/main.h:1401-1513

顺序堆分配器大小/备注
1Hemlis(非 final)sysMemSimpleAllocator(HEAP_MAIN)17 MB;Rocky 分析器在启动捕获时优先使用(注释:该内存为 GARLIC write-combine,CPU 访问慢)src/dev_ng/rage/base/src/profile/core.cpp:279-290
2Flex 堆sysMemFlexAllocate(sceKernelMapFlexibleMemory,CPU/GPU 读写)+ sysMemSimpleAllocator(HEAP_FLEX)22 MB src/dev_ng/rage/base/src/system/memory.cpp:1060-1071
3游戏虚拟堆 → 槽 0sysMemSimpleAllocator(HEAP_MAIN)596 MB(不扣碎片缓存)
4Chunky 堆sysMemSimpleAllocator(HEAP_CHUNKY),禁用小对象分配13 MB(注释“Decrease hemlis by 13mb”);存档、MNBVH 等以 ChunkyWrapper 临时切换使用 src/dev_ng/rage/base/src/system/memmanager.h:32-57
5Pool 堆sysMemSimpleAllocator(HEAP_MAIN),禁用小对象分配24 MB;经 sysMemStartPool/EndPool 切换,游戏用它分配脚本全局变量堆(大小来自 gameconfig 池项 scrGlobals)src/dev_ng/game/script/script.cpp:690-695
6资源堆 → 槽 1、2、3sysMemVirtualAllocator(2482 MB)(ENABLE_BUDDY_ALLOCATOR 仅 PC,故主机走此分支)64 KB 页 buddy src/dev_ng/rage/base/src/system/virtualallocator.h:23-31、src/dev_ng/rage/base/src/system/buddyallocator_config.h:12
7碎片缓存内存sysMemVirtualAllocate(FRAG_HEAP_SIZE)8 MB
8回放堆从 Flex 内存分配(注释“because we were OOM on PS4”)196 MB src/dev_ng/rage/base/src/system/main.h:1931-1933
9调试堆 → 槽 4sysMemVirtualAllocate220 MB,含 20 MB Recorder

3.3 Xbox One(Durango)#

src/dev_ng/rage/base/src/system/main.h:1515-1636

与 PS4 基本相同,差异:

  • 先 XMemSetAllocationHooks(sysMemXMemAlloc, sysMemXMemFree) 接管系统 XMem 分配 src/dev_ng/rage/base/src/system/main.h:1520。
  • 游戏虚拟堆用 VirtualAllocTracked(MEM_COMMIT|MEM_RESERVE|MEM_GRAPHICS|MEM_4MB_PAGES, PAGE_READWRITE|PAGE_GPU_COHERENT)——GPU 可见、4 MB 大页(注释“ONION”)src/dev_ng/rage/base/src/system/main.h:1559-1563。
  • 无 Flex 堆;回放堆来自 sysMemVirtualAllocate。
  • Dev/Bank 构建要求开发机开启“Profiling mode / Pix and Title”调试内存,否则 Quitf src/dev_ng/rage/base/src/system/main.h:1949-1961。

3.4 堆建立后的守卫#

  • PS4/XB1 调试堆构建在完成堆建立后递增 g_sysDirectVirtualEnabled/g_sysDirectDebugEnabled,此后任何代码再直接调用 sysMemVirtualAllocate 都会 Quitf("You are attempting to allocate system memory directly...") src/dev_ng/rage/base/src/system/main.h:1966-1969、src/dev_ng/rage/base/src/system/memory.cpp:1033-1043。
  • ENABLE_DEFRAGMENTATION 时把资源虚拟槽登记为 pgBase 的追踪堆 src/dev_ng/rage/base/src/system/main.h:1915-1917。
  • MEMORY_TRACKER 时在调试堆中创建系统分配追踪器 src/dev_ng/rage/base/src/system/main.h:2021-2026。
  • RunGame_OnEnter 中为四个主槽位做 UpdateMemorySnapshot(),作为加载完成后的基线 src/dev_ng/game/Core/app.cpp:1244-1247。

4. 分配器族#

类型用途关键行为证据
sysMemSimpleAllocator游戏虚拟堆、调试/回放/Flex/Chunky/Pool/Hemlis/Recorder首次匹配/最佳匹配的空闲链表,O(N) 分配、O(1) 释放并立即合并,16 字节对齐 + 16 字节头;新内存填 0xCD、释放填 0xDD(调试填充);heapId 用于区分分配 ID;默认分配失败即 Quitfsrc/dev_ng/rage/base/src/system/simpleallocator.h:25-117
sysSmallocator(内嵌于上者)≤128 字节的小对象每个尺寸档(步长 = 默认对齐)一个分配器,按 16 KB 对齐“chunk”成批切分无头小块,chunk 全空时归还;页位图覆盖最大 1 GB 堆(64 位平台)src/dev_ng/rage/base/src/system/smallocator.h:17-100、src/dev_ng/rage/base/src/system/simpleallocator.h:313-327
sysMemMultiAllocator顶层分配器Allocate(size, align, heapIndex) 按槽位转发;Free 查找拥有者,找不到则 Quitf(ERR_MEM_MULTIALLOC_FREE)src/dev_ng/rage/base/src/system/multiallocator.h:23-63、src/dev_ng/rage/base/src/system/multiallocator.cpp:78
sysMemBuddyAllocator / GrowBuddy / SparseBuddyPC 资源堆伙伴系统(叶大小 8 KB);Grow 版按需追加子堆;Sparse 版为每个叶记录 {memory, lockCount:8, userData:24, size}src/dev_ng/rage/base/src/system/buddyallocator.h:32-240
sysMemVirtualAllocatorPS4/XB1 资源堆64 KB 页(“最小化 TLB 缺失”)的 tiny buddy 堆,支持 AllocateMap(datResourceMap&) 一次映射整个资源src/dev_ng/rage/base/src/system/virtualallocator.h:18-80
sysMemSparseAllocatorPC dev @sparsegameheap 游戏堆调试用稀疏堆(便于捕捉越界);本页未展开src/dev_ng/rage/base/src/system/sparseallocator.h:16
CGameTempAllocatorsysMemManager 的 FragCache 分配器把 Allocate/Free 转发到 fragManager::GetFragCacheAllocator()->ExternalAllocate/ExternalFree;碎片缓存的 8 MB 内存来自上表的 FragMemorysrc/dev_ng/game/Core/game.h:21-35、src/dev_ng/game/Core/game.cpp:530-543、src/dev_ng/game/Core/game.cpp:1134-1140、src/dev_ng/rage/suite/src/fragment/manager.cpp:132-148

sysMemSimpleAllocator 的堆 ID 枚举:HEAP_MAIN=0, HEAP_NET=1, HEAP_DEBUG=2, HEAP_FLEX=3, HEAP_FRAG=4, HEAP_HEADER=5, HEAP_APP_1/2=6/7, HEAP_REPLAY=8, HEAP_CHUNKY=9 src/dev_ng/rage/base/src/system/simpleallocator.h:53-67。构造函数第四个参数 allowSmallAllocator 为 false 的堆(Chunky、Pool、Replay、Xenon Pool、Header)不使用小对象分配器 src/dev_ng/rage/base/src/system/simpleallocator.h:78-80。

4.1 当前分配器的切换#

“当前分配器”是线程局部变量(__THREAD sysMemAllocator_sm_Current/Master/Container)src/dev_ng/rage/base/src/system/memory.h:344-346;新线程继承创建者的当前分配器(见 threading-and-render-thread.md)。临时切换 API:

API效果证据
sysMemStartTemp/EndTemp切到 Master(多分配器),可嵌套src/dev_ng/rage/base/src/system/memory.cpp:247-275
sysMemStartDebug/EndDebug(USE_DEBUG_MEMORY())切到 MEMTYPE_DEBUG_VIRTUALsrc/dev_ng/rage/base/src/system/memory.cpp:277-303
sysMemStartRecorder/EndRecorder切到 Recorder 堆src/dev_ng/rage/base/src/system/memory.cpp:305-335
sysMemStartPool/EndPool(ENABLE_POOL_ALLOCATOR,仅 PS4/XB1)切到 Pool 堆,最多嵌套 8 层src/dev_ng/rage/base/src/system/memory.cpp:213-240、src/dev_ng/rage/base/src/system/memmanager.h:19-20
sysMemStartFlex/EndFlex仅 COMMERCE_CONTAINER(PS3)有效,其他平台为空宏——因此 CPedFactory/CVehicleFactory 里包裹池初始化的 sysMemStartFlex() 在当代平台上不起作用src/dev_ng/rage/base/src/system/memmanager.h:270-276、src/dev_ng/game/Peds/PedFactory.cpp:137-139

全局 operator new 在 RAGE_ENABLE_RAGE_NEW 下若未处于 RAGE_LOG_DISABLE 会断言“Call rage_new, not new”;对齐版 new 会把尺寸提升到至少为对齐值;operator_delete 交给 GetCurrent().Free()(多分配器会找到真正的拥有者)src/dev_ng/rage/base/src/system/main.h:2132-2235。非 final 构建支持 -breakonalloc/-breakonaddr 在特定分配序号或地址处中断 src/dev_ng/rage/base/src/system/main.cpp:1092-1111。

5. 内存桶#

分配会记账到“内存桶”(MEMBUCKET_*)。CSystem::Init 把纹理/模型记到 RENDER、phBound 到 PHYSICS、Scaleform 到 SCALEFORM、MoVE 到 ANIMATION src/dev_ng/game/system/system.cpp:281-288;各池初始化时显式指定桶(如世界实体 MEMBUCKET_WORLD、ped/载具 MEMBUCKET_GAMEPLAY);RunGame_OnEnter 在非 final 下给 16 个桶命名 src/dev_ng/game/Core/app.cpp:1237-1242。sysMemUseMemoryBucket 是作用域式切换器 src/dev_ng/rage/base/src/system/memory.h:287-300。

6. gameconfig.xml 与池大小配置#

6.1 fwConfigManager#

CSystem::Init 在 INIT_PARSER 之后调用 fwConfigManagerImpl<CGameConfig>::InitClass("common:/data/gameconfig.xml")(非 final 可 -altGameConfig=<path>)src/dev_ng/game/system/system.cpp:494-507。

机制实现证据
文件结构fwAllConfigs{ atArray<fwConfigWithFilter> m_ConfigArray };每项含 m_Build、m_Platforms、(非 final)m_Param 过滤器与一个 fwConfig*(实际为 CGameConfig),后者包含 m_PoolSizes.m_Entries[]{ m_PoolName, m_PoolSize }src/dev_ng/rage/framework/src/fwsys/config.psc:4-36、src/dev_ng/rage/framework/src/fwsys/config.h:211-328
加载与合并用 PARSER 读取;读到空对象 Quitf(ERR_GAMECONFIG_1);按数组顺序遍历,过滤器匹配的项先 MergeNonPoolData 合并普通字段,再把池大小写入 atMap<u32 hash, int>(后出现者覆盖先出现者)。文件不存在时静默使用默认值src/dev_ng/rage/framework/src/fwsys/config.cpp:75-130
过滤器构建:any/bank/bankrelease/beta/debug/dev/final/profile/release/nonfinal;平台:any/ps3/psn/psp2/x64/xbox360/xenon/xboxone/ps4;可用空格或逗号分隔多个,空串视为匹配;非 final 还可要求某个命令行参数存在;三者同时满足才生效src/dev_ng/rage/framework/src/fwsys/config.cpp:222-266
GetSizeOfPool(hash, default)有覆盖且未锁定时:sm_UseLargerPoolFromCodeOrData 为真则取 max(default, 覆盖值),否则断言覆盖值 ≥ 默认值并返回覆盖值;无覆盖时返回默认值src/dev_ng/rage/framework/src/fwsys/config.cpp:33-53
LockPoolSize声明某池不可配置;若配置文件仍列出它则断言/报错并标记为锁定(kLockedPoolSize = -1)src/dev_ng/rage/framework/src/fwsys/config.cpp:56-72
CONFIGURED_FROM_FILE值为 1 的默认大小占位符,表示“期望由配置文件给出”,避免程序员误以为改代码有效src/dev_ng/rage/framework/src/fwsys/config.h:332-336
fwConfig::m_ArchMaxModelInfos默认 32200(原 NUM_MODEL_INFOS),非负的覆盖值才合并src/dev_ng/rage/framework/src/fwsys/config.cpp:270-283

SetUseLargerPoolFromCodeOrData(true) 只被离线导出工具(导航网格、路网、音频网格、灯光提取)与音频遮挡构建路径调用,游戏运行时保持 false(检索结果)。

CGameConfig : fwConfig 在此基础上增加人口、2D 特效、模型信息(含默认玩家模型名)、扩展、流式引擎、在线服务(含 Steam App ID)、网络脚本广播数据、脚本栈大小、UGC 描述、媒体转码、线程设置(m_Threads)、若干玩法开关等配置 src/dev_ng/game/game/config.h:536-640。

本源码快照中没有 gameconfig.xml 数据文件(按文件名检索仅找到无关的网络工具与示例),因此标有 CONFIGURED_FROM_FILE 的池的实际大小无法从仓库得出。

6.2 读取池大小的时机#

  • CSystem::InitAfterConfigLoad()(gameconfig 加载后立即):fwArchetypeManager::SetMaxArchetypes(fwArchetypePooledMap)、CPoolHelpers::SetPedPoolSize(Peds)、SetVehiclePoolSize(Vehicles)、CVehiclePopulation::ms_maxNumberOfCarsInUse = VEHICLE_POOL_SIZE src/dev_ng/game/system/system.cpp:957-968。MAXNOOFPEDS 与 VEHICLE_POOL_SIZE 因此是运行时值(注释:“Used to be (90)/(200), but now it can be set through gameconfig.xml”)src/dev_ng/game/Peds/pedDefines.h:10-16、src/dev_ng/game/Vehicles/VehicleDefines.h:11-17。
  • 每个池的 InitPool():FW_INSTANTIATE_CLASS_POOL(_T, num, hashname) 生成的 InitPool 以 GetSizeOfPool(hashname, num) 作为容量——代码中的 num 是默认值/下限,配置文件可放大 src/dev_ng/rage/framework/src/fwtl/Pool.h:118-158。
  • 个别子系统直接读取池项作为非池用途的大小,例如脚本全局变量堆 scrGlobals、CTask 池数量 src/dev_ng/game/script/script.cpp:691、src/dev_ng/game/scene/scene.cpp:467-468。

7. 游戏对象池#

7.1 池的声明与存储#

  • 类内 FW_REGISTER_CLASS_POOL(_T) 声明静态 _ms_pPool 并重载 operator new/delete 走池;FW_REGISTER_LOCKING_CLASS_POOL 在 new/delete 外包临界区 src/dev_ng/rage/framework/src/fwtl/Pool.h:60-110。
  • 定义点使用 FW_INSTANTIATE_CLASS_POOL(定长)、FW_INSTANTIATE_BASECLASS_POOL(按 maxsize 容纳派生类)、*_SPILLOVER(溢出比例)、*_LOCKED_SIZE(禁止配置)等变体;InitPool 在指定内存桶下创建池并 RegisterInPoolTracker src/dev_ng/rage/framework/src/fwtl/Pool.h:118-188。game/ 中约有 199 处此类实例化(全文检索计数,含少量被注释掉的行)。
  • 存储分配:spillover 指针表从游戏虚拟槽分配;Xenon 优先使用 g_pXenonPoolAllocator(SIMPLE_POOL_SIZE 大小的池堆,Bank 构建为 7.5 MB 且来自调试内存,Release 为 0 即不创建 src/dev_ng/game/Core/main.cpp:156-175、src/dev_ng/rage/base/src/system/main.h:1668-1689),PS3 优先 residual/Flex 分配器;其余平台走 rage_new,即当前分配器(游戏虚拟堆);非 __NO_OUTPUT 时打印每个池的大小与地址区间 src/dev_ng/rage/framework/src/fwtl/Pool.cpp:460-526。因此在 PC/PS4/XB1 上,池内存是 474/596 MB 游戏堆的一部分,而不是主机上那个 24 MB 的 Pool 堆(后者只服务 sysMemStartPool 的显式调用者)。
  • 本代码库中没有名为 CPools 的集中类:残留的 CPools::Get*Pool() 调用都在注释中 src/dev_ng/game/Objects/Object.cpp:4361-4369。池以“每类一个静态池 + 若干集中初始化函数”的方式组织。

7.2 主要初始化点#

初始化点池证据
CGtaSceneInterface::InitPools()(CScene::Init 经场景接口调用)fwEntityContainer 描述符池、场景图池、CAnimatedBuilding、CBuilding、CObject、CCompEntity、CDummyObject、动态原型/实体组件、known-ref holder、CEntityBatch、CGrassBatch、CInteriorInst、CInteriorProxy、CPortalInst、拾取物与玻璃、各 MoVE 池化对象、IK、CPlayerInfo、CTask(TASK_HEAP_SIZE 当代平台 5 MB,数量取 gameconfig CTask)、任务序列、对象智能、巡逻路线、CEvent/CEventNetwork、路线、爆炸实例、粒子排序实体、载具玻璃、布料、CLightEntity、相机池、起落架、四叉树节点、事件/命令、LOD 容器、地图数据节点、附着扩展等src/dev_ng/game/scene/scene.cpp:377-519
同上(NAVMESH_EXPORT 导出碰撞时)放大的池:fwEntityContainer 描述符 65536、CBuilding 80000、CObject 11000、CDummyObject 30000src/dev_ng/game/scene/scene.cpp:404-416
CPedFactory 构造CPed(MAXNOOFPEDS)、CPedTargetting、CTaskInfo(堆 400 KB,数量 MAX_NUM_TASK_INFOS)、CChatHelper 等src/dev_ng/game/Peds/PedFactory.cpp:117-145
CPedIntelligenceFactoryCPedIntelligence(MAXNOOFPEDS)src/dev_ng/game/Peds/PedIntelligenceFactory.cpp:8
CVehicleFactory 构造CVehicle(变长池:堆 VEHICLE_HEAP_SIZE 当代平台 4640 KB,数量 VEHICLE_POOL_SIZE)、CWheel 等src/dev_ng/game/Vehicles/VehicleFactory.cpp:1443-1459

工厂在 CGame::Init 的 CreateFactories() 中创建、在 CGame::Shutdown 中销毁,因此 ped/载具池的生命周期是整个进程 src/dev_ng/game/Core/game.cpp:588-605。CGame::ShutdownLevel 结束时断言所有世界实体池(Vehicle/Ped/Object/DummyObject/Building/EntityBatch/GrassBatch/InteriorInst/InteriorProxy/PortalInst)为空 src/dev_ng/game/Core/game.cpp:1461-1471。

代码默认值示例(gameconfig 可覆盖):CQuadTreeNode 800、CArchetypeStreamSlot 255(注释:基础模型信息中只存 8 位)、CChatHelper 16;大量池(CObject、CCombatInfo、CRoadBlock、各扩展类)默认值为 CONFIGURED_FROM_FILE src/dev_ng/game/utils/QuadTree.cpp:11、src/dev_ng/game/modelinfo/ModelInfo.cpp:107-112、src/dev_ng/game/task/System/TaskHelpers.cpp:12927、src/dev_ng/game/Objects/Object.cpp:133。

池满时:rage_checked_pool_new(_T) 宏在池无空位时返回 NULL 而不调用构造 src/dev_ng/rage/framework/src/fwtl/Pool.h:190-191;__DEV 下 CPed、CVehicle、CTask 等注册了池满回调用于诊断 src/dev_ng/game/Peds/PedFactory.cpp:124-126、src/dev_ng/game/Vehicles/VehicleFactory.cpp:1455-1457。

8. 内存不足(OOM)处理#

场景行为证据
sysMemSimpleAllocator::Allocate 失败(默认 quit-on-fail)若设置了 sysMemOutOfMemoryDisplay 先调用它;然后 Quitf(非 final 打印堆 ID 与总/已用/可用;final 用错误码 ERR_MEM_EMBEDDEDALLOC_ALLOC)src/dev_ng/rage/base/src/system/simpleallocator.cpp:410-422
TryAllocate临时关闭 quit-on-fail,失败返回 NULLsrc/dev_ng/rage/base/src/system/simpleallocator.cpp:439-449
回放堆、Xenon 池堆、Header 堆构造后 SetQuitOnFail(false),失败返回 NULL 由调用者处理src/dev_ng/rage/base/src/system/main.h:1937-1939、src/dev_ng/rage/base/src/system/main.h:1685-1687、src/dev_ng/rage/base/src/system/main.h:1817-1818
PC 资源堆(GrowBuddy)不 Quit:先尝试所有子堆,再增长一次,仍失败则打印各子堆碎片信息、收缩 m_Size 并返回 NULLsrc/dev_ng/rage/base/src/system/buddyallocator.cpp:523-579
全局 operator_new 返回 NULL(HACK_GTA4 && !__NO_OUTPUT)打印游戏虚拟堆最大可用块与堆大小的警告src/dev_ng/rage/base/src/system/main.h:2114-2121
堆建立失败Unable to allocate %dK heap(ERR_MEM_EMBEDDEDALLOC_INIT_1);PC @debugMemory ≥ 2 GB;Durango 未开启调试内存src/dev_ng/rage/base/src/system/simpleallocator.cpp:92、src/dev_ng/rage/base/src/system/main.h:835-836、src/dev_ng/rage/base/src/system/main.h:1955-1958

CGame::OutOfMemoryDisplay() 只在 __BANK 构建中被挂到 sysMemOutOfMemoryDisplay src/dev_ng/game/Core/game.cpp:975-978。它“可能在任何线程、在部分系统已失效时被调用”,因此只读取缓存值:玩家缓存位置、相机位置/朝向、ped/载具数量,然后打印 CVehicle/CPed/CObject/CAnimatedBuilding::PrintSkeletonData()(骨骼数据往往是内存大户)与诊断上下文 src/dev_ng/game/Core/game.cpp:3827-3864。非 Bank 构建 OOM 时直接 Quitf,崩溃上下文由 coredump/Backtrace 路径记录(见 startup-and-app-fsm.md)。

开发期的内存压力与诊断工具:

  • -maponly/-maponlyextra/-cheapmode/-stealextramemory=<n>:在 devkit 上预先占用资源内存以模拟零售机余量(CFileMgr::AllocateDummyResourceMem/AllocateExtraResourceMem)src/dev_ng/game/Core/app.cpp:1101-1130。
  • 主机非 final 的 -actualmem 模拟零售内存配置;GetTotalRetailMemory/DevkitMemory 给出 PS4 4.5 GB/5.25 GB、XB1 5 GB 等常量 src/dev_ng/rage/base/src/system/main.h:511-575。
  • RAGE_MEMORY_DEBUG 下游戏中按 F11 打印所有堆以及网络堆、rline、SCxnMgr 分配器 src/dev_ng/game/Core/game.cpp:3341-3363。
  • -memvisualize 远程内存可视化、-logmemory 分配日志(见 startup-and-app-fsm.md)。

9. 其他专用内存#

  • 碎片缓存:CGame::Init 在“接触任何骨骼之前”调用 fragManager::CreateAllocator(),后者用 MEMMANAGER.GetFragMemory()(PC/主机由 InitGameHeap 预留的 8 MB;为空则从游戏虚拟堆分配)建立 fragCacheAllocator src/dev_ng/rage/suite/src/fragment/manager.cpp:132-148。
  • 回放堆:196 MB,供回放系统、Durango 媒体转码(MEDIA_MEMORY_WRAPPER.Init(..., GetReplayAllocator()))和 Orbis AVPlayer 使用 src/dev_ng/game/system/system.cpp:619-650、src/dev_ng/rage/framework/src/video/mediadecoder_orbis.cpp:423。
  • Xenon XMem 钩子:main.cpp 定义 NO_XMEM_WRAPPERS 并自行实现 XMemAlloc/XMemFree/XMemSize 转到 XMemAllocCustom 等 src/dev_ng/game/Core/main.cpp:212、src/dev_ng/game/Core/main.cpp:619-638;后者按分配器 ID(XHV 语音、XAudio2、XGraphics)分流,并允许 MeshBlend 与 PedHeadshot 通过 XGraphicsExtMemBufferHelper 预先提供缓冲 src/dev_ng/game/system/XMemWrapper.h:4-34、src/dev_ng/game/system/XMemWrapper.cpp:104-200。Durango 的对应机制是 XMemSetAllocationHooks(3.3 节)。
  • MemoryCheck:game/system/MemoryCheck.h/.cpp 定义了一种由 Tunables 下发、被 game/security 插件消费的检查记录结构,属于反作弊/反篡改范畴;本页不描述其内容,见 ../23-debug-security/security-overview.md。
  • Resource header:仅 PS3/Xenon(RESOURCE_HEADER)在 sysMemManager 中维护 8000 个块头信息(锁计数、用户数据)src/dev_ng/rage/base/src/data/resourceheader.h:13、src/dev_ng/rage/base/src/system/main.h:2028-2030。

10. 约束与边界行为#

  • 堆尺寸是编译期常量:除 PC 资源堆可增长、PC 调试堆可用 @debugMemory 调整外,所有堆在 main() 之前一次性建立,运行期不可扩展。
  • 主机禁止直接系统分配:堆建立后再直接 sysMemVirtualAllocate 会致命退出(调试堆构建)。新代码必须通过分配器或 sysMemManager 的专用堆。
  • 线程局部分配器:在调试堆或其他临时分配器激活时创建线程会断言;Durango 游戏线程必须手动重设分配器。
  • 池容量下限:GetSizeOfPool 断言配置值不小于代码默认值;把默认值写成 CONFIGURED_FROM_FILE(=1)的池若配置文件缺项,容量只有 1。
  • sysMemStartFlex 在当代平台为空:不要依赖它把分配移出游戏堆。

11. 未决问题 / 证据不足#

  • gameconfig.xml 缺失,无法给出 ped/载具/实体等池的实际容量。
  • sysMemVirtualAllocator、sysMemSparseAllocator、sysBuddyHeap 的内部算法未展开;碎片整理(USE_DEFRAGMENTATION)属于流式页面。
  • Hemlis 堆的命名含义源码中无说明;220 MB 分支是否在某些构建中生效未验证。
  • sysMemAllowResourceAlloc(Main_Prologue 置 false)的具体影响未追踪。