内存:堆拓扑、分配器与游戏池
本页描述 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。
背景与相关页面#
- 堆是在进程入口之前的静态构造中建立的:startup-and-app-fsm.md
- 线程继承分配器、线程优先级的 gameconfig 覆盖:threading-and-render-thread.md
- 世界实体池在
CScene::Init中初始化、在ShutdownLevel中必须清空:game-skeleton-and-main-loop.md - 实体类层次与各类池的使用:../05-world/world-and-entities.md、../06-peds/ped-architecture.md、../08-vehicles/vehicle-architecture.md
- 构建配置宏(
__DEV/__BANK/__FINAL等)矩阵:../00-overview/build-and-config.md
相关源码文件#
| 文件 | 作用 |
|---|---|
src/dev_ng/game/Core/main.cpp | 游戏定义的堆尺寸宏(KB) |
src/dev_ng/rage/base/src/system/main.h | InitGameHeap():各平台堆的创建顺序、sysMemMultiAllocator 组装、全局 operator new/delete |
src/dev_ng/rage/base/src/system/memory.h/.cpp | eMemoryType、FRAG_HEAP_SIZE、ENABLE_DEBUG_HEAP、sysMemStart*/End* 分配器切换、平台底层分配函数 |
src/dev_ng/rage/base/src/system/memmanager.h | sysMemManager:专用堆(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/.psc | fwConfigManager:从配置文件覆盖池大小 |
src/dev_ng/game/game/config.h/.cpp | CGameConfig(游戏配置,含线程与各子系统配置) |
src/dev_ng/rage/framework/src/fwtl/Pool.h/.cpp | FW_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 位平台:
| 宏 | PC | Durango (XB1) | Orbis (PS4) | 证据 |
|---|---|---|---|---|
SIMPLE_HEAP_SIZE 游戏虚拟堆 | 474 MB;USE_SPARSE_MEMORY(PC 64 位 __DEV)时 620 MB | 596 MB | 596 MB | src/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 MB | 2482 MB | src/dev_ng/game/Core/main.cpp:73-81、src/dev_ng/rage/base/src/system/buddyallocator_config.h:19-20 |
SIMPLE_VIRTUAL_SIZE 资源虚拟堆 | 1024 MB | 0 | 0 | src/dev_ng/game/Core/main.cpp:63-71 |
POOL_HEAP_SIZE | 24 MB(仅主机使用) | 24 MB | 24 MB | src/dev_ng/game/Core/main.cpp:31-33 |
FLEX_HEAP_SIZE | – | – | 22 MB | src/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_SIZE | 20 MB(ENABLE_DEBUG_HEAP) | 同左 | 同左 | src/dev_ng/game/Core/main.cpp:59-61 |
DEBUG_HEAP_SIZE | 220 MB(Xenon/PS3 为 60 MB) | 220 MB | 220 MB | src/dev_ng/game/Core/main.cpp:189-191 |
REPLAY_HEAP_SIZE(字节) | 196 MB | 196 MB | 196 MB | src/dev_ng/game/Core/main.cpp:194-196 |
FRAG_HEAP_SIZE(字节,memory.h) | 8 MB | 8 MB | 8 MB | src/dev_ng/rage/base/src/system/memory.h:41-50 |
PGKNOWNREFPOOLSIZE(条目) | 0x8800 | 0x8800 | 0x8800 | src/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 | 游戏虚拟堆 → 槽 0 | sysMemSimpleAllocator(HEAP_MAIN);非 final 且命令行含 @sparsegameheap 时改用 sysMemSparseAllocator | SIMPLE_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) → SetFragMemory | 8 MB src/dev_ng/rage/base/src/system/main.h:1832-1837 |
| 4 | 资源物理堆 → 槽 2、3 | sysMemGrowBuddyAllocator(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 | 资源虚拟堆 → 槽 1 | sysMemGrowBuddyAllocator(g_rscVirtualLeafSize, 1 GB);稀疏模式用 sysMemSparseBuddyAllocator | src/dev_ng/rage/base/src/system/main.h:1890-1911 |
| 6 | 回放堆 | sysMemVirtualAllocate + sysMemSimpleAllocator(HEAP_REPLAY),不 Quit | 196 MB;非 final @useDecreasedReplayMemory 减 24 MB src/dev_ng/rage/base/src/system/main.h:1919-1941 |
| 7 | 调试堆 → 槽 4 | sysMemDebugAllocate + 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
| 顺序 | 堆 | 分配器 | 大小/备注 |
|---|---|---|---|
| 1 | Hemlis(非 final) | sysMemSimpleAllocator(HEAP_MAIN) | 17 MB;Rocky 分析器在启动捕获时优先使用(注释:该内存为 GARLIC write-combine,CPU 访问慢)src/dev_ng/rage/base/src/profile/core.cpp:279-290 |
| 2 | Flex 堆 | sysMemFlexAllocate(sceKernelMapFlexibleMemory,CPU/GPU 读写)+ sysMemSimpleAllocator(HEAP_FLEX) | 22 MB src/dev_ng/rage/base/src/system/memory.cpp:1060-1071 |
| 3 | 游戏虚拟堆 → 槽 0 | sysMemSimpleAllocator(HEAP_MAIN) | 596 MB(不扣碎片缓存) |
| 4 | Chunky 堆 | sysMemSimpleAllocator(HEAP_CHUNKY),禁用小对象分配 | 13 MB(注释“Decrease hemlis by 13mb”);存档、MNBVH 等以 ChunkyWrapper 临时切换使用 src/dev_ng/rage/base/src/system/memmanager.h:32-57 |
| 5 | Pool 堆 | sysMemSimpleAllocator(HEAP_MAIN),禁用小对象分配 | 24 MB;经 sysMemStartPool/EndPool 切换,游戏用它分配脚本全局变量堆(大小来自 gameconfig 池项 scrGlobals)src/dev_ng/game/script/script.cpp:690-695 |
| 6 | 资源堆 → 槽 1、2、3 | sysMemVirtualAllocator(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 | 调试堆 → 槽 4 | sysMemVirtualAllocate | 220 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”调试内存,否则
Quitfsrc/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;默认分配失败即 Quitf | src/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 / SparseBuddy | PC 资源堆 | 伙伴系统(叶大小 8 KB);Grow 版按需追加子堆;Sparse 版为每个叶记录 {memory, lockCount:8, userData:24, size} | src/dev_ng/rage/base/src/system/buddyallocator.h:32-240 |
sysMemVirtualAllocator | PS4/XB1 资源堆 | 64 KB 页(“最小化 TLB 缺失”)的 tiny buddy 堆,支持 AllocateMap(datResourceMap&) 一次映射整个资源 | src/dev_ng/rage/base/src/system/virtualallocator.h:18-80 |
sysMemSparseAllocator | PC dev @sparsegameheap 游戏堆 | 调试用稀疏堆(便于捕捉越界);本页未展开 | src/dev_ng/rage/base/src/system/sparseallocator.h:16 |
CGameTempAllocator | sysMemManager 的 FragCache 分配器 | 把 Allocate/Free 转发到 fragManager::GetFragCacheAllocator()->ExternalAllocate/ExternalFree;碎片缓存的 8 MB 内存来自上表的 FragMemory | src/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_VIRTUAL | src/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_SIZEsrc/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在指定内存桶下创建池并RegisterInPoolTrackersrc/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 30000 | src/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 |
CPedIntelligenceFactory | CPedIntelligence(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,失败返回 NULL | src/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 并返回 NULL | src/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;为空则从游戏虚拟堆分配)建立fragCacheAllocatorsrc/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)的具体影响未追踪。