虚幻GAS底层原理解剖九 (内存管理)

发布于:2025-08-10 ⋅ 阅读:(42) ⋅ 点赞:(0)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

虚幻引擎中的 GAS(Gameplay Ability System) 是一个高性能、高扩展性的能力框架。为了支撑其复杂的技能、效果、属性和状态系统,它背后拥有一套精心设计的内存管理机制,包括:

  1. 结构体池化、对象复用
  2. 网络序列化内存优化
  3. 特定对象生命周期托管(Spec、Effect)
  4. 自定义 allocator、Arena 分配器
  5. Attribute 缓存机制
    子系统注册与 GC 管理

接下来我们分模块详细解析 GAS 是如何进行 内存管理和资源优化 的。

一、整体内存管理思路概览

GAS 的内存管理策略遵循以下原则:

原则 举例
对象复用 EffectSpecHandle、GameplayCueNotify
数据结构轻量化 FGameplayAttributeData 只保存基础值与当前值
结构体池化 目标数据、ModifierSpec 都来自池化内存
避免频繁GC 能力实例只创建一次,Effect 不主动GC
引用计数管理 GE 生命周期由 ASC 完整托管
Arena分配器 属性聚合使用 FAggregator 特制 allocator

二、核心对象的生命周期与托管逻辑

UGameplayAbility 的管理

所有 Ability 的实例(C++/BP)在 GiveAbility() 时被构造并缓存

每个 FGameplayAbilitySpec 中持有指针

在技能激活时不会重新创建实例,而是复用

TSharedPtr<UGameplayAbility> AbilityInstance = Spec.GetPrimaryInstance();

内存只在注册或删除时分配一次。

GameplayEffect 的内存管理

所有 GE(UGameplayEffect)作为配置类,只读资源,常驻内存

每次应用 GE 时,并不会直接创建对象,而是构建轻量运行体:

FGameplayEffectSpecHandle SpecHandle = MakeOutgoingSpec(...);
FGameplayEffectSpec 是轻量结构体,仅含:
指向 GE 定义指针(不复制类)
捕获的属性快照(值类型)
一些标志位和引用类型(如 TargetData)

避免了 UE 常见 UObject 开销,支持池化。

ActiveGameplayEffect 生命周期

FActiveGameplayEffectsContainer ActiveGameplayEffects;

所有激活中的 GE 存放于此容器

GE 的添加/更新/移除由 ASC 内部完成

不暴露给外部直接操作(防止悬挂指针)

生命周期结束自动清除,不走垃圾回收(避免 GC 开销)

三、属性(Attribute)缓存与更新机制

每个属性是 FGameplayAttributeData,它只包含两个 float 值:

float BaseValue;
float CurrentValue;

所有属性更新通过 FAggregator 进行聚合处理:

// 聚合器计算路径
FAggregator → Mod Stack → Base + Add + Mult + Override → Current

特点:

  1. 聚合器使用轻量结构 + 内嵌 Arena Allocator
  2. 修改属性不触发对象创建,只更新浮点值
  3. 属性变化自动触发 NetDeltaSerialize,减少带宽与内存分配

四、TargetData 与 EffectContext 的池化与复用

FGameplayAbilityTargetDataHandle

  1. 用于传输目标信息的结构体

  2. 包含多个 FGameplayAbilityTargetData 派生类(如 TargetActorArray)

  3. 所有派生结构体均支持结构体序列化,无 UObject 开销

  4. 通过 TSharedPtr 管理生命周期(引用计数)

FGameplayEffectContextHandle

  1. 表示 GE 的施加上下文(来源 Actor、命中位置等)

  2. 内部指向 FGameplayEffectContext(非 UObject)

  3. 通过 TSharedPtr 管理,可嵌套复制

  4. 支持网络复制与再利用

这些都是 轻量非 UObject 结构体,避免 UE GC 开销

五、网络同步中的内存优化

GAS 中有两个重要的结构体用于同步:

结构体 用途
FGameplayAttributeData 属性同步(NetDeltaSerialize)
FActiveGameplayEffectHandle 效果同步(GE ID + 生命周期)

优化机制:

  1. 使用 FastArraySerializer 同步 GE 列表,只同步变化项

  2. 所有同步数据支持 NetSerialize(),避免复制整个对象

  3. 使用 PackedBitWriter 实现高压缩的浮点数传输

  4. 不同步 UObject,只传递指针索引、标签、数值等

六、对象池和自定义 Allocator 的应用

GAS 使用了多种自定义分配器:

  1. 聚合器分配器 FAggregatorRef:
FGameplayEffectSpec::Captures → 聚合器系统 → ArenaAllocator

用于批量构建临时 Modifier 聚合数据,生命周期绑定到 GE Spec。

  1. GE 缓存:
    UGameplayEffect 被作为资源缓存,使用标准 UObject 生命周期,但不频繁加载/卸载。

  2. TagContainer:
    FGameplayTagContainer 使用值类型 + 显式引用共享,不走 UObject。

七、垃圾回收与非 GC 对象管理

GAS 中大量使用 非 UObject 的结构体 + 智能指针:

类型 GC 管理方式
UGameplayAbility / GE 常驻资源,由 ASC 或系统持有
FGameplayEffectSpec / TargetData 非 GC 对象,TSharedPtr 管理
AttributeSet UObject,会注册到 ASC 的 SubObject 管理器中
GameplayCueNotify 使用 Object Pool 缓存重用(如 FX)

总结:GAS 尽可能避开 GC,使用结构体和智能指针进行生命周期控制

总结:GAS 内存管理的关键设计点

设计点 实现方式
复用对象,避免频繁创建 技能/GE/特效对象仅实例化一次
尽量结构体化 Spec/TargetData 均为结构体(TSharedPtr 管理)
避免 GC 压力 除 Ability 外几乎无 UObject 生命周期
内存池化 TargetData、Aggregator 使用 Arena 分配器
高效网络结构 NetDeltaSerialize + PackedBitWriter
生命周期集中托管 ASC 中统一管理技能/GE/属性/标签的状态

网站公告

今日签到

点亮在社区的每一天
去签到