From 8c3f2c99cc1ea85edbd76bf28b1cd420ee6cc1d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=80=9D=E6=B5=B7?= <1464576565@qq.com> Date: Thu, 23 Apr 2026 18:18:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96MemoryPool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Runtime/MemoryPool/MemoryPool.Core.cs | 139 ++++++++++++++++++++--- Runtime/MemoryPool/MemoryPool.cs | 12 +- Runtime/MemoryPool/MemoryPoolRegistry.cs | 102 +++++++++++++---- Runtime/UI/Constant/UIResRegistry.cs | 4 - 4 files changed, 212 insertions(+), 45 deletions(-) diff --git a/Runtime/MemoryPool/MemoryPool.Core.cs b/Runtime/MemoryPool/MemoryPool.Core.cs index 1f37ee1..cd8ef4f 100644 --- a/Runtime/MemoryPool/MemoryPool.Core.cs +++ b/Runtime/MemoryPool/MemoryPool.Core.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; namespace AlicizaX @@ -6,9 +7,28 @@ namespace AlicizaX public static class MemoryPool where T : class, IMemory, new() { + private sealed class ReferenceComparer : IEqualityComparer + { + public static readonly ReferenceComparer Instance = new ReferenceComparer(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(T x, T y) + { + return ReferenceEquals(x, y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetHashCode(T obj) + { + return RuntimeHelpers.GetHashCode(obj); + } + } + private static T[] s_Stack = Array.Empty(); private static int s_Count; private static int s_MaxCapacity = 2048; + private static Dictionary s_InPoolSet; + private static int s_StrictCheckVersion = -1; // ---- 回收策略状态 ---- private static int s_HighWaterMark; @@ -30,8 +50,8 @@ namespace AlicizaX static MemoryPool() { MemoryPoolRegistry.Register(typeof(T), new MemoryPoolRegistry.MemoryPoolHandle( - acquire: () => Acquire(), - release: obj => Release((T)obj), + acquire: AcquireAsMemory, + release: ReleaseAsMemory, clear: ClearAll, prewarm: Prewarm, getInfo: GetInfo, @@ -40,6 +60,18 @@ namespace AlicizaX )); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static IMemory AcquireAsMemory() + { + return Acquire(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ReleaseAsMemory(IMemory memory) + { + Release((T)memory); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Acquire() { @@ -54,6 +86,7 @@ namespace AlicizaX int idx = --s_Count; T item = s_Stack[idx]; s_Stack[idx] = null; + RemoveFromStrictCheckSet(item); return item; } @@ -67,24 +100,14 @@ namespace AlicizaX return new T(); } - [MethodImpl(MethodImplOptions.NoInlining)] - private static bool ContainsInPool(T item) - { - for (int i = 0; i < s_Count; i++) - { - if (ReferenceEquals(s_Stack[i], item)) - return true; - } - - return false; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Release(T item) { if (item == null) return; - if (MemoryPool.EnableStrictCheck && ContainsInPool(item)) + EnsureStrictCheckState(); + + if (MemoryPool.EnableStrictCheck && s_InPoolSet.ContainsKey(item)) throw new InvalidOperationException($"MemoryPool<{typeof(T).Name}>: Double release detected."); s_ReleaseCount++; @@ -100,6 +123,7 @@ namespace AlicizaX if (s_Count == s_Stack.Length) Grow(); + AddToStrictCheckSet(item); s_Stack[s_Count++] = item; } @@ -132,6 +156,7 @@ namespace AlicizaX int removeCount = Math.Max((int)(excess * ratio), 1); int newCount = s_Count - removeCount; + RemoveRangeFromStrictCheckSet(newCount, removeCount); Array.Clear(s_Stack, newCount, removeCount); s_Count = newCount; @@ -184,7 +209,9 @@ namespace AlicizaX while (s_Count < count) { - s_Stack[s_Count++] = new T(); + T item = new T(); + s_Stack[s_Count++] = item; + AddToStrictCheckSet(item); s_CreateCount++; } } @@ -194,6 +221,7 @@ namespace AlicizaX if (keepCount >= s_Count) return; keepCount = Math.Max(keepCount, 0); + RemoveRangeFromStrictCheckSet(keepCount, s_Count - keepCount); Array.Clear(s_Stack, keepCount, s_Count - keepCount); s_Count = keepCount; TryShrinkArray(); @@ -206,6 +234,7 @@ namespace AlicizaX public static void ClearAll() { + ResetStrictCheckSet(); Array.Clear(s_Stack, 0, s_Count); s_Count = 0; s_HighWaterMark = s_CurrentInUse; @@ -231,5 +260,83 @@ namespace AlicizaX s_HighWaterMark, s_MaxCapacity, s_IdleFrames, s_Stack.Length); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void EnsureStrictCheckState() + { + int version = MemoryPool.StrictCheckVersion; + if (s_StrictCheckVersion == version) + return; + + s_StrictCheckVersion = version; + if (!MemoryPool.EnableStrictCheck) + { + s_InPoolSet = null; + return; + } + + RebuildStrictCheckSet(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void RebuildStrictCheckSet() + { + int capacity = s_Count > 0 ? s_Count : 4; + var set = new Dictionary(capacity, ReferenceComparer.Instance); + for (int i = 0; i < s_Count; i++) + { + T item = s_Stack[i]; + if (item != null) + set[item] = 0; + } + + s_InPoolSet = set; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void AddToStrictCheckSet(T item) + { + EnsureStrictCheckState(); + if (!MemoryPool.EnableStrictCheck || item == null) + return; + + s_InPoolSet[item] = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void RemoveFromStrictCheckSet(T item) + { + EnsureStrictCheckState(); + if (!MemoryPool.EnableStrictCheck || item == null) + return; + + s_InPoolSet.Remove(item); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void RemoveRangeFromStrictCheckSet(int startIndex, int count) + { + EnsureStrictCheckState(); + if (!MemoryPool.EnableStrictCheck || count <= 0) + return; + + int endIndex = startIndex + count; + for (int i = startIndex; i < endIndex; i++) + { + T item = s_Stack[i]; + if (item != null) + s_InPoolSet.Remove(item); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ResetStrictCheckSet() + { + EnsureStrictCheckState(); + if (!MemoryPool.EnableStrictCheck) + return; + + s_InPoolSet.Clear(); + } } } diff --git a/Runtime/MemoryPool/MemoryPool.cs b/Runtime/MemoryPool/MemoryPool.cs index d5b31bb..5832093 100644 --- a/Runtime/MemoryPool/MemoryPool.cs +++ b/Runtime/MemoryPool/MemoryPool.cs @@ -8,6 +8,7 @@ namespace AlicizaX public static partial class MemoryPool { private static bool _enableStrictCheck; + private static int _strictCheckVersion; /// /// 获取或设置是否开启强制检查。 @@ -15,9 +16,18 @@ namespace AlicizaX public static bool EnableStrictCheck { get => _enableStrictCheck; - set => _enableStrictCheck = value; + set + { + if (_enableStrictCheck == value) + return; + + _enableStrictCheck = value; + _strictCheckVersion++; + } } + internal static int StrictCheckVersion => _strictCheckVersion; + /// /// 获取内存池的数量。 /// diff --git a/Runtime/MemoryPool/MemoryPoolRegistry.cs b/Runtime/MemoryPool/MemoryPoolRegistry.cs index 935b7c1..0855008 100644 --- a/Runtime/MemoryPool/MemoryPoolRegistry.cs +++ b/Runtime/MemoryPool/MemoryPoolRegistry.cs @@ -9,22 +9,28 @@ namespace AlicizaX { internal sealed class MemoryPoolHandle { - public readonly Func Acquire; - public readonly Action Release; - public readonly Action Clear; - public readonly Action Prewarm; - public readonly Func GetInfo; - public readonly Action Tick; - public readonly Action Shrink; + public delegate IMemory AcquireHandler(); + public delegate void ReleaseHandler(IMemory memory); + public delegate void ClearHandler(); + public delegate void IntHandler(int value); + public delegate MemoryPoolInfo GetInfoHandler(); + + public readonly AcquireHandler Acquire; + public readonly ReleaseHandler Release; + public readonly ClearHandler Clear; + public readonly IntHandler Prewarm; + public readonly GetInfoHandler GetInfo; + public readonly IntHandler Tick; + public readonly IntHandler Shrink; public MemoryPoolHandle( - Func acquire, - Action release, - Action clear, - Action prewarm, - Func getInfo, - Action tick, - Action shrink) + AcquireHandler acquire, + ReleaseHandler release, + ClearHandler clear, + IntHandler prewarm, + GetInfoHandler getInfo, + IntHandler tick, + IntHandler shrink) { Acquire = acquire; Release = release; @@ -39,7 +45,7 @@ namespace AlicizaX private static readonly Dictionary s_Handles = new Dictionary(64); - private static Action[] s_TickArray = Array.Empty>(); + private static MemoryPoolHandle.IntHandler[] s_TickArray = Array.Empty(); private static int s_TickCount; private static bool s_TickArrayDirty; @@ -56,11 +62,13 @@ namespace AlicizaX /// public static IMemory Acquire(Type type) { + if (type == null) + throw new ArgumentNullException(nameof(type)); + if (s_Handles.TryGetValue(type, out var handle)) return handle.Acquire(); - RuntimeHelpers.RunClassConstructor( - typeof(MemoryPool<>).MakeGenericType(type).TypeHandle); + EnsureRegistered(type); if (s_Handles.TryGetValue(type, out handle)) return handle.Acquire(); @@ -80,11 +88,15 @@ namespace AlicizaX return; } - RuntimeHelpers.RunClassConstructor( - typeof(MemoryPool<>).MakeGenericType(type).TypeHandle); + EnsureRegistered(type); if (s_Handles.TryGetValue(type, out handle)) + { handle.Release(memory); + return; + } + + throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type."); } public static MemoryPoolInfo[] GetAllInfos() @@ -104,32 +116,62 @@ namespace AlicizaX public static void Prewarm(Type type, int count) { + if (type == null) + throw new ArgumentNullException(nameof(type)); + if (s_Handles.TryGetValue(type, out var handle)) { handle.Prewarm(count); return; } - RuntimeHelpers.RunClassConstructor( - typeof(MemoryPool<>).MakeGenericType(type).TypeHandle); + EnsureRegistered(type); if (s_Handles.TryGetValue(type, out handle)) + { handle.Prewarm(count); + return; + } + + throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type."); } public static void ClearType(Type type) { - if (s_Handles.TryGetValue(type, out var handle)) - handle.Clear(); + if (type == null) + throw new ArgumentNullException(nameof(type)); + + if (!s_Handles.TryGetValue(type, out var handle)) + { + EnsureRegistered(type); + if (!s_Handles.TryGetValue(type, out handle)) + throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type."); + } + + handle.Clear(); } public static void RemoveFromType(Type type, int count) { + if (type == null) + throw new ArgumentNullException(nameof(type)); + if (s_Handles.TryGetValue(type, out var handle)) { int unused = handle.GetInfo().UnusedCount; handle.Shrink(unused - count); + return; } + + EnsureRegistered(type); + if (s_Handles.TryGetValue(type, out handle)) + { + int unused = handle.GetInfo().UnusedCount; + handle.Shrink(unused - count); + return; + } + + throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type."); } @@ -146,7 +188,7 @@ namespace AlicizaX { s_TickCount = s_Handles.Count; if (s_TickArray.Length < s_TickCount) - s_TickArray = new Action[s_TickCount]; + s_TickArray = new MemoryPoolHandle.IntHandler[s_TickCount]; int i = 0; foreach (var kv in s_Handles) @@ -154,5 +196,17 @@ namespace AlicizaX s_TickArrayDirty = false; } + + private static void EnsureRegistered(Type type) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + if (!typeof(IMemory).IsAssignableFrom(type)) + throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type."); + + RuntimeHelpers.RunClassConstructor( + typeof(MemoryPool<>).MakeGenericType(type).TypeHandle); + } } } diff --git a/Runtime/UI/Constant/UIResRegistry.cs b/Runtime/UI/Constant/UIResRegistry.cs index 576d226..3d468c6 100644 --- a/Runtime/UI/Constant/UIResRegistry.cs +++ b/Runtime/UI/Constant/UIResRegistry.cs @@ -52,10 +52,6 @@ namespace AlicizaX.UI.Runtime return TryReflectAndRegisterInternal(holderType, out info); } - /// - /// Internal method to reflect and register UI resource without logging. - /// Used by both runtime fallback and pre-registration. - /// [MethodImpl(MethodImplOptions.NoInlining)] private static bool TryReflectAndRegisterInternal(Type holderType, out UIResInfo info) {