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

文章目录

  • 前言
  • 一、整体内存管理思路概览
  • 二、核心对象的生命周期与托管逻辑
    • UGameplayAbility 的管理
    • GameplayEffect 的内存管理
    • ActiveGameplayEffect 生命周期
  • 三、属性(Attribute)缓存与更新机制
  • 四、TargetData 与 EffectContext 的池化与复用
  • 五、网络同步中的内存优化
  • 六、对象池和自定义 Allocator 的应用
  • 七、垃圾回收与非 GC 对象管理
  • 总结:GAS 内存管理的关键设计点


前言

虚幻引擎中的 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 管理
AttributeSetUObject,会注册到 ASC 的 SubObject 管理器中
GameplayCueNotify使用 Object Pool 缓存重用(如 FX)

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/94753.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/94753.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/94753.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Rust 通用库新增 WebAssembly

1 先判断&#xff1a;也许你的 crate 已经能跑 Wasm&#xff01;排查阻碍因素 直接文件/网络 I/O块式&#xff08;同步&#xff09;I/Ostd::thread 线程创建并不受支持的 C 系统库绑定快速验证rustup target add wasm32-unknown-unknown cargo build --target wasm32-unknown-…

java分布式定时任务

一、分布式锁的底层实现细节&#xff08;以 Redis 为例&#xff09;分布式锁是解决任务重复执行的核心&#xff0c;需保证原子性、超时释放和可重入性。以下是生产级 Redis 锁实现&#xff1a;public class RedisDistributedLock {private final RedisTemplate<String, Stri…

Kafka 的基本操作(1)

Kafka 是一个分布式流处理平台&#xff0c;核心功能是高吞吐量的消息发布与订阅。以下是 Kafka 最常用的基本操作&#xff0c;涵盖环境启动、主题管理、消息生产与消费等核心场景&#xff08;基于 Kafka 2.x 版本&#xff0c;使用命令行工具&#xff09;。 一、环境准备与启动 …

React 为什么要自定义 Hooks?

历史相关文章2024年&#xff1a; React 为什么引入 Hooks &#xff1f; React 中&#xff0c;Hook 是一个特定的概念 自定义 Hook&#xff08;Custom Hook&#xff09;在 React 中相当于&#xff1a; ✅ 一个可以复用的逻辑片段&#xff0c;封装了多个内置 Hooks 的组合和行为 …

[激光原理与应用-181]:测量仪器 - 频谱型 - 干涉仪,OCT(光学相干断层扫描技术)

OCT&#xff08;光学相干断层扫描技术&#xff09;的核心工作原理基于低相干光干涉&#xff0c;通过测量生物组织或材料内部不同深度结构的背向散射光信号差异&#xff0c;构建高分辨率的二维或三维图像。以下是其工作原理的详细解析&#xff1a;一、基础原理&#xff1a;低相干…

python学智能算法(三十五)|SVM-软边界拉格朗日方程乘子非负性理解

【1】引言 前序学习进程中&#xff0c;已经学习了构建SVM软边界拉格朗日方程&#xff0c;具体方程形式为&#xff1a; L(w,b,ξ,α,μ)12∣∣w∣∣2C∑i1nξi−∑i1nαi[yi(w⋅xib)−1ξi]−∑i1nμiξiL(w,b,\xi,\alpha,\mu)\frac{1}{2}||w||^2C\sum_{i1}^{n}\xi_{i}-\sum_{i…

LeetCode 刷题【34. 在排序数组中查找元素的第一个和最后一个位置、35. 搜索插入位置】

34. 在排序数组中查找元素的第一个和最后一个位置 自己做 解&#xff1a;二分查找 class Solution { public://二分查找int halfFind(vector<int> nums, int begin, int end, int target){if(begin > end) //找不到的情况return -1;int mid (begin end) / …

Vue3 计算属性与监听器

文章目录计算属性配置项 computedHTML 结构Vue 实例数据方法计算属性绑定数据和方法完整代码vue3商品加减案例监听器配置项 watch简单类型写法深度监听写法计算属性配置项 computed 使用 Vue 实现一个商品价格计算器&#xff0c;设置一个初始单价&#xff0c;初始数量为 1&…

Mysql如何迁移数据库数据

文章目录一、使用 mysqldump 工具&#xff08;最常用&#xff09;&#xff08;一&#xff09;导出数据&#xff08;二&#xff09;导出数据库&#xff08;不含数据&#xff09;&#xff08;三&#xff09;导出指定表&#xff08;四&#xff09;导入数据二、直接拷贝文件三、使用…

为什么输入 URL 后会显示页面?HTTP 协议的 “幕后操作”

&#x1f680; 浏览器输入URL后&#xff0c;到底发生了什么&#xff1f;前端面试HTTP协议深度解析 今天咱们不聊八卦&#xff0c;来点硬核的——前端面试中绕不开的HTTP协议。是不是一提到“浏览器输入URL后发生了什么”&#xff0c;你就开始头大&#xff1f;别担心&#xff0c…

内网穿透原理和部署教程

前言&#xff1a;本文介绍了内网穿透技术原理及frp工具的部署方法。由于NAT映射表是临时且单向的&#xff0c;外网无法直接访问内网服务。通过部署公网服务器作为中转&#xff0c;frp实现了内网服务的穿透访问。具体步骤包括&#xff1a;下载frp软件包&#xff0c;详细说明了配…

Ping32:为企业数据安全筑起铜墙铁壁​

Ping32&#xff1a;为企业数据安全筑起铜墙铁壁在数字经济飞速发展的今天&#xff0c;企业数据已成为核心竞争力的重要组成部分。然而&#xff0c;数据泄露事件频发&#xff0c;给企业带来的损失难以估量。从商业机密外泄到客户信息曝光&#xff0c;每一次数据安全事故都可能让…

2025年国内iPaaS平台精选

在过去几年里&#xff0c;许多企业在业务系统中面临了诸多有关集成的难题&#xff1a;系统建好了&#xff0c;数据流不动&#xff1b;接口打通了&#xff0c;流程仍卡顿&#xff1b;工具堆叠越来越多&#xff0c;但协同效率反而走低。 这并不是架构设计的问题&#xff0c;也不是…

AD绘制PCB之-板外形设计

1、通过机械层1 【Mechanical 1】绘制出板子轮廓2、选中上面绘制得轮廓先选中一条边&#xff0c;然后按tab键&#xff0c;可以自动选择这条边闭合得线条3、按照选择对象定义设计--->板子形状------>按照选择对象定义执行后得效果&#xff1a;4、根据需要设置板子四角为半…

《汇编语言:基于X86处理器》第12章 浮点数处理与指令编码(2)

Intel X86架构数据的运算主要由通用寄存器处理&#xff0c;但浮点数例外&#xff0c;浮点数的运算由专门的FPU寄存器处理。二进制浮点数由三部分组成&#xff1a;符号&#xff0c;有效数字和阶码。这些格式都出自由IEEE组织制定的标准754-1985&#xff1a;以下是三种浮点数的格…

vue3通过按钮实现横向滚动、鼠标滚动横坐标滚动

效果图&#xff1a;可点击左右文字进行滚动、或通过滚动鼠标 内容左右滚动<template><div class"Home"><div style"display: flex;height: 100%;align-items: center;"><div click"scrollLeft()" style"width: 80px;t…

【Agent】AutoGen:LLM驱动的多Agent对话框架

文章目录一、AutoGen简介1.1 AutoGen的特点1.2 AutoGen的实现1.2.1 可对话Agent1.2.2 对话编程二、基于AutoGen构建多智能体系统2.1 构建步骤2.1 协作模式2.2 通信模型2.3 人机协同2.4 具体示例参考资料一、AutoGen简介 AutoGen是微软推出的一个Multi-Agent框架&#xff0c;允…

乙巳年闰六月十六凌晨感怀

乙巳年闰六月十六凌晨感怀 一段历程一段情&#xff0c;儿郎峥嵘儿郎行。 岁月流金建功业&#xff0c;春秋风尚能潮赢。 路途苦乐人生度&#xff0c;评说成败当下名。 百年孤寂留水墨&#xff0c;千载独步守安宁。

Redis 分布式Session

一、引入依赖引入spring-session-data-redis依赖&#xff0c;不需要指定version&#xff0c;默认和springboot的version保持一致<!-- Spring Session Redis --> <dependency><groupId>org.springframework.session</groupId><artifactId>spring…

JAVA实现附件分片上传

项目需求由于文件服务器的限制&#xff0c;单次调用文件上传接口上传的附件的大小不能超过500MB&#xff0c;对于超过500MB的附件需要分片上传程序示例private Boolean uploadFile(File uploadFile, String uploadUrl, List<Object> fileList) {final long CHUNK_SIZE 5…