diff --git a/GFramework.Core/pool/README.md b/GFramework.Core/pool/README.md index 2e5527e..7ea5147 100644 --- a/GFramework.Core/pool/README.md +++ b/GFramework.Core/pool/README.md @@ -4,49 +4,945 @@ GFramework 的对象池系统是一个高效的内存管理机制,旨在减少垃圾回收(GC)压力,通过复用对象实例来提高应用程序性能。该系统实现了对象的创建、获取、释放和销毁的完整生命周期管理。 +**核心优势:** + +- **减少 GC 压力**:复用对象实例,避免频繁的内存分配和回收 +- **提高性能**:避免重复创建开销大的对象 +- **灵活管理**:支持多个独立的对象池,按需分类管理 +- **自动生命周期**:与 System 生命周期集成,自动管理对象销毁 +- **类型安全**:基于泛型实现,编译时类型检查 + ## 核心组件 ### IPoolableObject 接口 -定义了可池化对象的行为规范,所有池化对象都需要实现此接口: +定义了可池化对象的行为规范,所有需要池化的对象都必须实现此接口。 -- `OnAcquire()`: 当对象从池中获取时调用,用于初始化或重置对象状态 -- `OnRelease()`: 当对象被放回池中时调用,用于清理对象状态 -- `OnPoolDestroy()`: 当对象池被销毁时调用,用于执行最终清理 +```csharp +public interface IPoolableObject +{ + /// + /// 当对象从池中获取时调用,用于初始化或重置对象状态 + /// + void OnAcquire(); + + /// + /// 当对象被放回池中时调用,用于清理对象状态 + /// + void OnRelease(); + + /// + /// 当对象池被销毁时调用,用于执行最终清理 + /// + void OnPoolDestroy(); +} +``` + +**生命周期:** + +``` +创建 → Acquire(从池取出)→ 使用 → Release(放回池)→ 可再次 Acquire + ↓ + Pool Destroy → OnPoolDestroy → 销毁 +``` ### IObjectPoolSystem 接口 -定义了对象池系统的基本操作: +定义了对象池系统的基本操作接口。 -- `Acquire(TKey key)`: 从指定键的对象池中获取一个对象 -- `Release(TKey key, TObject obj)`: 将对象释放回指定键的对象池 -- `Clear()`: 清空所有对象池 +```csharp +public interface IObjectPoolSystem + where TObject : IPoolableObject + where TKey : notnull +{ + /// + /// 从指定键的对象池中获取一个对象 + /// + /// 对象池的键值 + /// 获取到的对象实例 + TObject Acquire(TKey key); + + /// + /// 将对象释放回指定键的对象池中 + /// + /// 对象池的键值 + /// 需要释放的对象 + void Release(TKey key, TObject obj); + + /// + /// 清空所有对象池,销毁所有池中的对象 + /// + void Clear(); +} +``` ### AbstractObjectPoolSystem 抽象类 -实现了 IObjectPoolSystem 接口的具体逻辑: +实现了 `IObjectPoolSystem` 接口的具体逻辑,提供了对象池管理的完整实现。 + +**核心特性:** - 使用字典存储多个对象池,以键区分不同的对象池 +- 使用栈(Stack)存储池中的对象,实现 LIFO(后进先出)管理 - 提供获取和释放对象的方法 -- 通过抽象方法 Create 让子类决定如何创建对象 +- 通过抽象方法 `Create` 让子类决定如何创建对象 - 在系统销毁时自动清理所有对象池 -## 设计特点 +**内部实现:** -1. **类型安全**: 使用泛型参数确保类型安全 -2. **多池管理**: 支持使用不同键管理多个对象池 -3. **生命周期管理**: 通过接口方法精确控制对象的生命周期 -4. **内存效率**: 通过复用对象减少内存分配和垃圾回收 +```csharp +public abstract class AbstractObjectPoolSystem + : AbstractSystem, IObjectPoolSystem + where TObject : IPoolableObject + where TKey : notnull +{ + // 存储对象池的字典,键为池标识,值为对应类型的对象栈 + protected readonly Dictionary> Pools = new(); -## 工作原理 + // 获取对象 + public TObject Acquire(TKey key) + { + if (!Pools.TryGetValue(key, out var pool)) + { + pool = new Stack(); + Pools[key] = pool; + } -1. **获取对象** (Acquire): 如果池中有可用对象,则从池中取出;否则创建新对象 -2. **释放对象** (Release): 调用对象的 OnRelease 方法后将其放回池中 -3. **创建对象**: 通过抽象方法 Create 由子类实现具体的创建逻辑 -4. **清理**: 在系统销毁时调用 OnDestroy 方法清理所有对象池 + var obj = pool.Count > 0 + ? pool.Pop() // 从池中取出 + : Create(key); // 创建新对象 + + obj.OnAcquire(); // 调用对象的获取钩子 + return obj; + } + + // 释放对象 + public void Release(TKey key, TObject obj) + { + obj.OnRelease(); // 调用对象的释放钩子 + + if (!Pools.TryGetValue(key, out var pool)) + { + pool = new Stack(); + Pools[key] = pool; + } + + pool.Push(obj); // 放回池中 + } + + // 清空所有对象池 + public void Clear() + { + foreach (var obj in Pools.Values.SelectMany(pool => pool)) + { + obj.OnPoolDestroy(); // 调用对象的销毁钩子 + } + + Pools.Clear(); + } + + // 子类实现:创建新对象 + protected abstract TObject Create(TKey key); + + // 系统销毁时自动清空对象池 + protected override void OnDestroy() + { + Clear(); + } +} +``` + +## 基本使用 + +### 1. 定义池化对象 + +首先,创建一个实现 `IPoolableObject` 接口的类。 + +```csharp +public class Bullet : IPoolableObject +{ + public int Damage { get; private set; } + public float Speed { get; private set; } + public Vector3 Position { get; private set; } + public Vector3 Direction { get; private set; } + public bool IsActive { get; private set; } + + public void OnAcquire() + { + // 从池中获取时调用,初始化对象状态 + IsActive = true; + } + + public void OnRelease() + { + // 放回池中时调用,清理对象状态 + IsActive = false; + Damage = 0; + Speed = 0; + Position = Vector3.Zero; + Direction = Vector3.Zero; + } + + public void OnPoolDestroy() + { + // 对象池销毁时调用,执行最终清理 + // 可以在这里释放非托管资源 + } + + // 设置子弹属性的方法 + public void Setup(int damage, float speed, Vector3 position, Vector3 direction) + { + Damage = damage; + Speed = speed; + Position = position; + Direction = direction; + } + + // 更新子弹逻辑 + public void Update(float deltaTime) + { + Position += Direction * Speed * deltaTime; + } +} +``` + +### 2. 实现对象池系统 + +继承 `AbstractObjectPoolSystem` 并实现 `Create` 方法。 + +```csharp +public class BulletPoolSystem : AbstractObjectPoolSystem +{ + protected override Bullet Create(string key) + { + // 根据键值创建不同类型的子弹 + return key switch + { + "standard" => new Bullet(), + "heavy" => new Bullet(), + "explosive" => new Bullet(), + _ => new Bullet() + }; + } + + protected override void OnInit() + { + // 可以预先创建一些对象放入池中 + for (int i = 0; i < 10; i++) + { + var bullet = Create("standard"); + bullet.OnAcquire(); + bullet.OnRelease(); + Release("standard", bullet); + } + } +} +``` + +### 3. 在架构中注册对象池系统 + +```csharp +public class GameArchitecture : Architecture +{ + protected override void Init() + { + // 注册其他组件 + RegisterModel(new PlayerModel()); + RegisterSystem(new CombatSystem()); + + // 注册对象池系统 + RegisterSystem(new BulletPoolSystem()); + } +} +``` + +### 4. 使用对象池 + +```csharp +public class ShootingSystem : AbstractSystem +{ + private BulletPoolSystem _bulletPool; + private List _activeBullets = new(); + + protected override void OnInit() + { + _bulletPool = this.GetSystem(); + this.RegisterEvent(OnShoot); + this.RegisterEvent(OnUpdate); + } + + private void OnShoot(ShootEvent e) + { + // 从对象池获取子弹 + var bullet = _bulletPool.Acquire("standard"); + + // 设置子弹属性 + bullet.Setup( + damage: e.Damage, + speed: 10.0f, + position: e.StartPosition, + direction: e.Direction + ); + + // 添加到活跃列表 + _activeBullets.Add(bullet); + } + + private void OnUpdate(GameUpdateEvent e) + { + // 更新所有活跃子弹 + for (int i = _activeBullets.Count - 1; i >= 0; i--) + { + var bullet = _activeBullets[i]; + bullet.Update(e.DeltaTime); + + // 检查子弹是否需要销毁(例如:超出范围或击中目标) + if (ShouldDestroyBullet(bullet)) + { + // 放回对象池 + _bulletPool.Release("standard", bullet); + _activeBullets.RemoveAt(i); + } + } + } + + private bool ShouldDestroyBullet(Bullet bullet) + { + // 简单示例:子弹超出一定范围则销毁 + return bullet.Position.Length() > 1000.0f; + } +} +``` + +## 高级用法 + +### 1. 多键对象池管理 + +```csharp +public class ParticlePoolSystem : AbstractObjectPoolSystem +{ + protected override Particle Create(string key) + { + // 根据键值创建不同类型的粒子效果 + return key switch + { + "explosion" => new Particle(explosionPrefab), + "smoke" => new Particle(smokePrefab), + "spark" => new Particle(sparkPrefab), + _ => throw new ArgumentException($"Unknown particle type: {key}") + }; + } + + // 提供便捷方法 + public Particle SpawnExplosion(Vector3 position) + { + var particle = Acquire("explosion"); + particle.Position = position; + particle.Play(); + return particle; + } +} + +// 使用 +public class EffectSystem : AbstractSystem +{ + private ParticlePoolSystem _particlePool; + + protected override void OnInit() + { + _particlePool = this.GetSystem(); + } + + public void PlayExplosion(Vector3 position) + { + _particlePool.SpawnExplosion(position); + } +} +``` + +### 2. 动态对象池管理 + +```csharp +public class EnemyPoolSystem : AbstractObjectPoolSystem +{ + protected override Enemy Create(string key) + { + // 根据敌人类型创建不同的敌人 + var enemyPrefab = LoadEnemyPrefab(key); + return new Enemy(enemyPrefab); + } + + // 动态注册新的敌人类型池 + public void RegisterEnemyType(string enemyType) + { + if (!Pools.ContainsKey(enemyType)) + { + Pools[enemyType] = new Stack(); + + // 预热:预先创建几个敌人放入池中 + for (int i = 0; i < 3; i++) + { + var enemy = Create(enemyType); + enemy.OnAcquire(); + enemy.OnRelease(); + Release(enemyType, enemy); + } + } + } +} + +// 使用 +public class EnemySpawnerSystem : AbstractSystem +{ + private EnemyPoolSystem _enemyPool; + + protected override void OnInit() + { + _enemyPool = this.GetSystem(); + + // 注册不同类型的敌人 + _enemyPool.RegisterEnemyType("goblin"); + _enemyPool.RegisterEnemyType("orc"); + _enemyPool.RegisterEnemyType("dragon"); + } + + public void SpawnEnemy(string enemyType, Vector3 position) + { + var enemy = _enemyPool.Acquire(enemyType); + enemy.Position = position; + enemy.Activate(); + } +} +``` + +### 3. 对象池大小限制 + +```csharp +public class LimitedBulletPoolSystem : AbstractObjectPoolSystem +{ + private const int MaxPoolSize = 50; + + protected override Bullet Create(string key) + { + return new Bullet(); + } + + public new void Release(string key, Bullet obj) + { + // 检查对象池大小 + if (Pools.TryGetValue(key, out var pool) && pool.Count >= MaxPoolSize) + { + // 池已满,不回收对象,让它被 GC 回收 + return; + } + + // 调用基类的 Release 方法 + base.Release(key, obj); + } +} +``` + +### 4. 对象池统计和调试 + +```csharp +public class DebuggablePoolSystem : AbstractObjectPoolSystem +{ + public Dictionary PoolSizes => Pools.ToDictionary( + kvp => kvp.Key, + kvp => kvp.Value.Count + ); + + public int TotalPooledObjects => Pools.Values.Sum(stack => stack.Count); + + public void LogPoolStatus() + { + foreach (var (key, stack) in Pools) + { + Console.WriteLine($"Pool [{key}]: {stack.Count} objects"); + } + + Console.WriteLine($"Total pooled objects: {TotalPooledObjects}"); + } + + protected override void OnDestroy() + { + LogPoolStatus(); + base.OnDestroy(); + } +} +``` ## 使用场景 -- 频繁创建和销毁的临时对象 -- 高性能要求的系统,需要减少GC压力 -- 对象创建成本较高的情况 \ No newline at end of file +### 1. 游戏对象池 + +**适用对象:** +- 子弹、箭矢、投射物 +- 敌人、NPC +- 爆炸效果、粒子系统 +- UI 元素(提示、对话框) + +**示例:子弹池** + +```csharp +// 定义子弹 +public class Bullet : IPoolableObject +{ + public Vector3 Position { get; private set; } + public Vector3 Velocity { get; private set; } + public float Lifetime { get; private set; } + + public void OnAcquire() + { + Lifetime = 5.0f; // 5秒后自动销毁 + } + + public void OnRelease() + { + Position = Vector3.Zero; + Velocity = Vector3.Zero; + } + + public void OnPoolDestroy() { } + + public void Update(float deltaTime) + { + Position += Velocity * deltaTime; + Lifetime -= deltaTime; + } +} + +// 子弹对象池 +public class BulletPoolSystem : AbstractObjectPoolSystem +{ + protected override Bullet Create(string key) => new Bullet(); +} + +// 射击系统 +public class ShootingSystem : AbstractSystem +{ + private BulletPoolSystem _bulletPool; + private List _activeBullets = new(); + + protected override void OnInit() + { + _bulletPool = this.GetSystem(); + this.RegisterEvent(OnShoot); + this.RegisterEvent(OnUpdate); + } + + private void OnShoot(ShootEvent e) + { + var bullet = _bulletPool.Acquire("normal"); + bullet.Position = e.StartPosition; + bullet.Velocity = e.Direction * e.Speed; + _activeBullets.Add(bullet); + } + + private void OnUpdate(GameUpdateEvent e) + { + for (int i = _activeBullets.Count - 1; i >= 0; i--) + { + var bullet = _activeBullets[i]; + bullet.Update(e.DeltaTime); + + if (bullet.Lifetime <= 0) + { + _bulletPool.Release("normal", bullet); + _activeBullets.RemoveAt(i); + } + } + } +} +``` + +### 2. UI 元素池 + +**适用对象:** +- 对话框 +- 提示框 +- 菜单项 +- 列表项 + +**示例:提示框池** + +```csharp +public class Tooltip : IPoolableObject +{ + public string Text { get; set; } + public bool IsActive { get; private set; } + + public void OnAcquire() + { + IsActive = true; + } + + public void OnRelease() + { + IsActive = false; + Text = ""; + } + + public void OnPoolDestroy() { } + + public void Show(string text, Vector3 position) + { + Text = text; + // 更新 UI 位置和内容 + } +} + +public class TooltipPoolSystem : AbstractObjectPoolSystem +{ + protected override Tooltip Create(string key) => new Tooltip(); +} + +public class UISystem : AbstractSystem +{ + private TooltipPoolSystem _tooltipPool; + + protected override void OnInit() + { + _tooltipPool = this.GetSystem(); + } + + public void ShowTooltip(string text, Vector3 position) + { + var tooltip = _tooltipPool.Acquire("default"); + tooltip.Show(text, position); + } +} +``` + +### 3. 网络消息对象池 + +**适用对象:** +- 网络包 +- 协议消息 +- 数据包 + +**示例:网络包池** + +```csharp +public class NetworkPacket : IPoolableObject +{ + public byte[] Data { get; private set; } + public int Length { get; private set; } + + public void OnAcquire() + { + Data = Array.Empty(); + Length = 0; + } + + public void OnRelease() + { + // 清理敏感数据 + if (Data != null) + { + Array.Clear(Data, 0, Data.Length); + } + Length = 0; + } + + public void OnPoolDestroy() { } + + public void SetData(byte[] data) + { + Data = data; + Length = data.Length; + } +} + +public class PacketPoolSystem : AbstractObjectPoolSystem +{ + protected override NetworkPacket Create(string key) => new NetworkPacket(); +} +``` + +## 最佳实践 + +### 1. 对象生命周期管理 + +```csharp +// ✅ 好的做法:确保所有对象都放回池中 +public class BulletSystem : AbstractSystem +{ + private List _activeBullets = new(); + + protected override void OnDestroy() + { + // 系统销毁时,确保所有活跃子弹都放回池中 + foreach (var bullet in _activeBullets) + { + _bulletPool.Release("standard", bullet); + } + + _activeBullets.Clear(); + base.OnDestroy(); + } +} + +// ❌ 不好的做法:忘记放回对象,导致泄漏 +public class BadBulletSystem : AbstractSystem +{ + private List _activeBullets = new(); + + private void OnUpdate(GameUpdateEvent e) + { + // 子弹销毁时忘记放回池中 + if (bullet.Lifetime <= 0) + { + _activeBullets.RemoveAt(i); + // 忘记调用 _bulletPool.Release(...) + } + } +} +``` + +### 2. 对象状态重置 + +```csharp +// ✅ 好的做法:在 OnRelease 中彻底重置对象状态 +public class Bullet : IPoolableObject +{ + public int Damage { get; set; } + public float Speed { get; set; } + public List Tags { get; set; } + public Dictionary Data { get; set; } + + public void OnRelease() + { + // 重置所有属性 + Damage = 0; + Speed = 0; + Tags?.Clear(); + Data?.Clear(); + + // 也可以设置为新实例(如果性能允许) + Tags = new List(); + Data = new Dictionary(); + } +} + +// ❌ 不好的做法:不完全重置状态 +public class BadBullet : IPoolableObject +{ + public List Tags = new List(); + + public void OnRelease() + { + // 只清空列表,但列表实例本身保留 + // 这可能导致问题:如果其他代码持有列表引用 + Tags.Clear(); + } +} +``` + +### 3. 对象池预热 + +```csharp +// ✅ 好的做法:预先创建一些对象放入池中 +public class BulletPoolSystem : AbstractObjectPoolSystem +{ + protected override Bullet Create(string key) => new Bullet(); + + protected override void OnInit() + { + // 为常用的子弹类型预热对象池 + var commonTypes = new[] { "standard", "heavy", "sniper" }; + + foreach (var type in commonTypes) + { + // 预先创建 5 个对象 + for (int i = 0; i < 5; i++) + { + var bullet = Create(type); + bullet.OnAcquire(); + bullet.OnRelease(); + Release(type, bullet); + } + } + } +} +``` + +### 4. 对象池大小管理 + +```csharp +// ✅ 好的做法:限制对象池大小,避免内存浪费 +public class BoundedPoolSystem : AbstractObjectPoolSystem +{ + private const int MaxPoolSize = 100; + + public new void Release(string key, PooledObject obj) + { + if (Pools.TryGetValue(key, out var pool) && pool.Count >= MaxPoolSize) + { + // 池已满,不回收对象 + return; + } + + base.Release(key, obj); + } +} + +// ✅ 好的做法:动态调整对象池大小 +public class AdaptivePoolSystem : AbstractObjectPoolSystem +{ + private Dictionary _peakUsage = new(); + + public new void Release(string key, PooledObject obj) + { + // 记录峰值使用量 + if (Pools.TryGetValue(key, out var pool)) + { + _peakUsage[key] = Math.Max(_peakUsage.GetValueOrDefault(key, 0), pool.Count); + } + + // 根据使用情况动态调整 + int maxAllowed = _peakUsage.GetValueOrDefault(key, 10) * 2; + if (pool.Count >= maxAllowed) + { + return; // 不回收 + } + + base.Release(key, obj); + } +} +``` + +### 5. 调试和监控 + +```csharp +// ✅ 好的做法:添加对象池统计功能 +public class MonitoredPoolSystem : AbstractObjectPoolSystem +{ + private Dictionary _acquireCount = new(); + private Dictionary _releaseCount = new(); + + public new PooledObject Acquire(string key) + { + _acquireCount[key] = _acquireCount.GetValueOrDefault(key, 0) + 1; + return base.Acquire(key); + } + + public new void Release(string key, PooledObject obj) + { + _releaseCount[key] = _releaseCount.GetValueOrDefault(key, 0) + 1; + base.Release(key, obj); + } + + public void PrintStatistics() + { + foreach (var key in Pools.Keys) + { + int acquired = _acquireCount.GetValueOrDefault(key, 0); + int released = _releaseCount.GetValueOrDefault(key, 0); + int leaked = acquired - released; + + Console.WriteLine($"Pool [{key}]:"); + Console.WriteLine($" Acquired: {acquired}"); + Console.WriteLine($" Released: {released}"); + Console.WriteLine($" Leaked: {leaked}"); + Console.WriteLine($" Pool size: {Pools[key].Count}"); + } + } +} +``` + +## 性能优化 + +### 1. 减少对象创建 + +```csharp +// ✅ 好的做法:对象池预热,避免运行时创建 +public class PreheatedPoolSystem : AbstractObjectPoolSystem +{ + protected override Bullet Create(string key) => new Bullet(); + + protected override void OnInit() + { + // 预热常用对象池 + PreheatPool("standard", 20); + PreheatPool("heavy", 10); + } + + private void PreheatPool(string key, int count) + { + for (int i = 0; i < count; i++) + { + var bullet = Create(key); + bullet.OnAcquire(); + bullet.OnRelease(); + Release(key, bullet); + } + } +} +``` + +### 2. 使用值类型作为键 + +```csharp +// ✅ 好的做法:使用枚举或整数作为键,避免字符串比较 +public enum BulletType +{ + Standard, + Heavy, + Explosive +} + +public class FastBulletPoolSystem : AbstractObjectPoolSystem +{ + protected override Bullet Create(BulletType key) => key switch + { + BulletType.Standard => new Bullet(), + BulletType.Heavy => new Bullet(), + BulletType.Explosive => new Bullet(), + _ => throw new ArgumentException() + }; +} +``` + +### 3. 批量操作 + +```csharp +// ✅ 好的做法:批量获取和释放对象 +public class BatchPoolSystem : AbstractObjectPoolSystem +{ + public List AcquireBatch(string key, int count) + { + var objects = new List(count); + for (int i = 0; i < count; i++) + { + objects.Add(Acquire(key)); + } + return objects; + } + + public void ReleaseBatch(string key, IEnumerable objects) + { + foreach (var obj in objects) + { + Release(key, obj); + } + } +} +``` + +## 注意事项 + +1. **对象状态管理**:确保 `OnRelease` 方法彻底重置对象状态,避免状态污染 +2. **对象引用**:不要在放回对象池后继续持有对象引用 +3. **线程安全**:对象池本身不是线程安全的,如需多线程访问,需要自行加锁 +4. **内存泄漏**:确保所有获取的对象最终都放回池中 +5. **对象池大小**:合理设置对象池大小,避免内存浪费或频繁创建 + +## 相关包 + +- [`system`](../system/README.md) - 对象池系统继承自 AbstractSystem +- [`architecture`](../architecture/README.md) - 在架构中注册对象池系统 + +--- + +**许可证**: Apache 2.0