using System; using System.Runtime.CompilerServices; namespace AlicizaX { public static class MemoryPool where T : class, IMemory, new() { private static T[] s_Stack = Array.Empty(); private static int s_Count; private static int s_MaxCapacity = 2048; // ---- 回收策略状态 ---- private static int s_HighWaterMark; private static int s_RecentAcquireCount; private static int s_IdleFrames; private static int s_LastTickFrame; private static int s_PeakInUse; private static int s_CurrentInUse; private const int IDLE_THRESHOLD = 300; // ~5s @60fps private const int IDLE_AGGRESSIVE = 900; // ~15s @60fps private const int MIN_KEEP = 4; // ---- 统计计数器 ---- private static int s_AcquireCount; private static int s_ReleaseCount; private static int s_CreateCount; static MemoryPool() { MemoryPoolRegistry.Register(typeof(T), new MemoryPoolRegistry.MemoryPoolHandle( acquire: () => Acquire(), release: obj => Release((T)obj), clear: ClearAll, prewarm: Prewarm, getInfo: GetInfo, tick: Tick, shrink: Shrink )); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Acquire() { s_AcquireCount++; s_RecentAcquireCount++; s_CurrentInUse++; if (s_CurrentInUse > s_PeakInUse) s_PeakInUse = s_CurrentInUse; if (s_Count > 0) { int idx = --s_Count; T item = s_Stack[idx]; s_Stack[idx] = null; return item; } return CreateNew(); } [MethodImpl(MethodImplOptions.NoInlining)] private static T CreateNew() { s_CreateCount++; 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)) throw new InvalidOperationException($"MemoryPool<{typeof(T).Name}>: Double release detected."); s_ReleaseCount++; if (s_CurrentInUse > 0) s_CurrentInUse--; item.Clear(); if (s_Count >= s_MaxCapacity) return; if (s_Count == s_Stack.Length) Grow(); s_Stack[s_Count++] = item; } internal static void Tick(int frameCount) { if (frameCount == s_LastTickFrame) return; s_LastTickFrame = frameCount; if (s_PeakInUse > s_HighWaterMark) s_HighWaterMark = s_PeakInUse; if (s_RecentAcquireCount == 0) s_IdleFrames++; else s_IdleFrames = 0; s_RecentAcquireCount = 0; if (s_Count <= MIN_KEEP) return; if (s_IdleFrames >= IDLE_THRESHOLD) { int target = Math.Max((int)(s_HighWaterMark * 1.5f), MIN_KEEP); if (s_Count > target) { int excess = s_Count - target; float ratio = s_IdleFrames < IDLE_AGGRESSIVE ? 0.25f : 0.5f; int removeCount = Math.Max((int)(excess * ratio), 1); int newCount = s_Count - removeCount; Array.Clear(s_Stack, newCount, removeCount); s_Count = newCount; TryShrinkArray(); } if (s_IdleFrames >= IDLE_AGGRESSIVE) { s_HighWaterMark = Math.Max(s_HighWaterMark >> 1, MIN_KEEP); s_PeakInUse = s_CurrentInUse; } } } [MethodImpl(MethodImplOptions.NoInlining)] private static void Grow() { int newLen = s_Stack.Length == 0 ? 8 : s_Stack.Length << 1; if (newLen > s_MaxCapacity) newLen = s_MaxCapacity; var newStack = new T[newLen]; Array.Copy(s_Stack, 0, newStack, 0, s_Count); s_Stack = newStack; } [MethodImpl(MethodImplOptions.NoInlining)] private static void TryShrinkArray() { if (s_Stack.Length > 32 && s_Count < s_Stack.Length >> 2) { int newLen = Math.Max(s_Count << 1, 8); var newStack = new T[newLen]; Array.Copy(s_Stack, 0, newStack, 0, s_Count); s_Stack = newStack; } } public static void Prewarm(int count) { count = Math.Min(count, s_MaxCapacity); if (count <= s_Count) return; if (count > s_Stack.Length) { var newStack = new T[count]; Array.Copy(s_Stack, 0, newStack, 0, s_Count); s_Stack = newStack; } while (s_Count < count) { s_Stack[s_Count++] = new T(); s_CreateCount++; } } public static void Shrink(int keepCount) { if (keepCount >= s_Count) return; keepCount = Math.Max(keepCount, 0); Array.Clear(s_Stack, keepCount, s_Count - keepCount); s_Count = keepCount; TryShrinkArray(); } public static void SetMaxCapacity(int max) { s_MaxCapacity = Math.Max(max, MIN_KEEP); } public static void ClearAll() { Array.Clear(s_Stack, 0, s_Count); s_Count = 0; s_HighWaterMark = s_CurrentInUse; s_PeakInUse = s_CurrentInUse; s_IdleFrames = 0; s_RecentAcquireCount = 0; s_Stack = Array.Empty(); } public static int UnusedCount { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => s_Count; } internal static MemoryPoolInfo GetInfo() { return new MemoryPoolInfo( typeof(T), s_Count, s_CurrentInUse, s_AcquireCount, s_ReleaseCount, s_CreateCount, s_HighWaterMark, s_MaxCapacity, s_IdleFrames, s_Stack.Length); } } }