重构Class内存池

This commit is contained in:
陈思海 2026-04-21 14:24:36 +08:00
parent 10de7e040f
commit 2ac929c364
11 changed files with 622 additions and 420 deletions

View File

@ -1,4 +1,4 @@
using AlicizaX;
using AlicizaX;
using System;
using System.Collections.Generic;
using System.IO;
@ -34,20 +34,44 @@ namespace AlicizaX.Editor
t.EnableStrictCheck = enableStrictCheck;
}
EditorGUILayout.LabelField("Reference Pool Count", MemoryPool.Count.ToString());
EditorGUILayout.LabelField("Memory Pool Count", MemoryPool.Count.ToString());
m_ShowFullClassName = EditorGUILayout.Toggle("Show Full Class Name", m_ShowFullClassName);
m_ReferencePoolInfos.Clear();
// 全局统计
MemoryPoolInfo[] referencePoolInfos = MemoryPool.GetAllMemoryPoolInfos();
int totalUnused = 0, totalUsing = 0, totalArrayLen = 0;
foreach (var info in referencePoolInfos)
{
totalUnused += info.UnusedCount;
totalUsing += info.UsingCount;
totalArrayLen += info.PoolArrayLength;
}
EditorGUILayout.LabelField("Total Cached", totalUnused.ToString());
EditorGUILayout.LabelField("Total In Use", totalUsing.ToString());
EditorGUILayout.LabelField("Total Array Capacity", totalArrayLen.ToString());
EditorGUILayout.Space();
// 全局操作按钮
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Clear All Pools"))
{
MemoryPoolRegistry.ClearAll();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
// 按 Assembly 分组
m_ReferencePoolInfos.Clear();
foreach (MemoryPoolInfo referencePoolInfo in referencePoolInfos)
{
string assemblyName = referencePoolInfo.Type.Assembly.GetName().Name;
List<MemoryPoolInfo> results = null;
if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out results))
if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out List<MemoryPoolInfo> results))
{
results = new List<MemoryPoolInfo>();
m_ReferencePoolInfos.Add(assemblyName, results);
}
results.Add(referencePoolInfo);
}
@ -58,20 +82,16 @@ namespace AlicizaX.Editor
if (currentState != lastState)
{
if (currentState)
{
m_OpenedItems.Add(assemblyReferencePoolInfo.Key);
}
else
{
m_OpenedItems.Remove(assemblyReferencePoolInfo.Key);
}
}
if (currentState)
{
EditorGUILayout.BeginVertical("box");
{
var label = "Unused\tUsing.\tAcquire\tRelease\tAdd\tRemove";
var label = "Unused\tUsing\tAcquire\tRelease\tCreated\tHiWater\tMaxCap\tIdle\tArrLen";
EditorGUILayout.LabelField(m_ShowFullClassName ? "Full Class Name" : "Class Name", label);
assemblyReferencePoolInfo.Value.Sort(Comparison);
foreach (MemoryPoolInfo referencePoolInfo in assemblyReferencePoolInfo.Value)
@ -81,31 +101,34 @@ namespace AlicizaX.Editor
if (GUILayout.Button("Export CSV Data"))
{
string exportFileName = EditorUtility.SaveFilePanel("Export CSV Data", string.Empty, Utility.Text.Format("Reference Pool Data - {0}.csv", assemblyReferencePoolInfo.Key), string.Empty);
string exportFileName = EditorUtility.SaveFilePanel("Export CSV Data", string.Empty, Utility.Text.Format("Memory Pool Data - {0}.csv", assemblyReferencePoolInfo.Key), string.Empty);
if (!string.IsNullOrEmpty(exportFileName))
{
try
{
int index = 0;
string[] data = new string[assemblyReferencePoolInfo.Value.Count + 1];
data[index++] = "Class Name,Full Class Name,Unused,Using,Acquire,Release,Add,Remove";
foreach (MemoryPoolInfo referencePoolInfo in assemblyReferencePoolInfo.Value)
data[index++] = "Class Name,Full Class Name,Unused,Using,Acquire,Release,Created,HighWaterMark,MaxCapacity,IdleFrames,ArrayLength";
foreach (MemoryPoolInfo info in assemblyReferencePoolInfo.Value)
{
data[index++] = Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7}", referencePoolInfo.Type.Name, referencePoolInfo.Type.FullName, referencePoolInfo.UnusedMemoryCount, referencePoolInfo.UsingMemoryCount, referencePoolInfo.AcquireMemoryCount, referencePoolInfo.ReleaseMemoryCount, referencePoolInfo.AddMemoryCount, referencePoolInfo.RemoveMemoryCount);
data[index++] = Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10}",
info.Type.Name, info.Type.FullName,
info.UnusedCount, info.UsingCount,
info.AcquireCount, info.ReleaseCount,
info.CreateCount, info.HighWaterMark,
info.MaxCapacity, info.IdleFrames, info.PoolArrayLength);
}
File.WriteAllLines(exportFileName, data, Encoding.UTF8);
Debug.Log(Utility.Text.Format("Export reference pool CSV data to '{0}' success.", exportFileName));
Debug.Log(Utility.Text.Format("Export memory pool CSV data to '{0}' success.", exportFileName));
}
catch (Exception exception)
{
Debug.LogError(Utility.Text.Format("Export reference pool CSV data to '{0}' failure, exception is '{1}'.", exportFileName, exception));
Debug.LogError(Utility.Text.Format("Export memory pool CSV data to '{0}' failure, exception is '{1}'.", exportFileName, exception));
}
}
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.Separator();
}
}
@ -125,26 +148,23 @@ namespace AlicizaX.Editor
m_EnableStrictCheck = serializedObject.FindProperty("m_EnableStrictCheck");
}
private void DrawReferencePoolInfo(MemoryPoolInfo referencePoolInfo)
private void DrawReferencePoolInfo(MemoryPoolInfo info)
{
#if UNITY_6000_0_OR_NEWER
EditorGUILayout.LabelField(m_ShowFullClassName ? referencePoolInfo.Type.FullName : referencePoolInfo.Type.Name, Utility.Text.Format("{0,-12}\t{1,-12}\t{2,-12}\t{3,-12}\t{4}\t{5}", referencePoolInfo.UnusedMemoryCount, referencePoolInfo.UsingMemoryCount, referencePoolInfo.AcquireMemoryCount, referencePoolInfo.ReleaseMemoryCount, referencePoolInfo.AddMemoryCount, referencePoolInfo.RemoveMemoryCount));
#else
EditorGUILayout.LabelField(m_ShowFullClassName ? referencePoolInfo.Type.FullName :
referencePoolInfo.Type.Name, Utility.Text.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}",referencePoolInfo.UnusedMemoryCount, referencePoolInfo.UsingMemoryCount, referencePoolInfo.AcquireMemoryCount, referencePoolInfo.ReleaseMemoryCount, referencePoolInfo.AddMemoryCount, referencePoolInfo.RemoveMemoryCount));
#endif
string name = m_ShowFullClassName ? info.Type.FullName : info.Type.Name;
string values = Utility.Text.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8}",
info.UnusedCount, info.UsingCount,
info.AcquireCount, info.ReleaseCount,
info.CreateCount, info.HighWaterMark,
info.MaxCapacity, info.IdleFrames, info.PoolArrayLength);
EditorGUILayout.LabelField(name, values);
}
private int Comparison(MemoryPoolInfo a, MemoryPoolInfo b)
{
if (m_ShowFullClassName)
{
return a.Type.FullName.CompareTo(b.Type.FullName);
}
else
{
return a.Type.Name.CompareTo(b.Type.Name);
}
}
}
}

View File

@ -0,0 +1,231 @@
using System;
using System.Runtime.CompilerServices;
namespace AlicizaX
{
public static class MemoryPool<T> where T : class, IMemory, new()
{
private static T[] s_Stack = Array.Empty<T>();
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.AggressiveInlining)]
public static void Release(T item)
{
if (item == null) return;
s_ReleaseCount++;
// 防止 ClearAll 后 Release 导致负数
if (s_CurrentInUse > 0)
s_CurrentInUse--;
item.Clear();
if (s_Count >= s_MaxCapacity)
return;
if (MemoryPool.EnableStrictCheck && s_Count > 0 && ReferenceEquals(s_Stack[s_Count - 1], item))
throw new InvalidOperationException($"MemoryPool<{typeof(T).Name}>: Double release detected.");
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;
// 更新高水位:基于实际并发使用量(在外对象数),而非单帧 Acquire 次数
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;
// 阶段1温和回收 / 阶段2激进回收
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;
}
}
/// <summary>
/// 预热池,启动时调用避免运行时 GC。
/// </summary>
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++;
}
}
/// <summary>
/// 缩容:场景切换时调用,释放多余内存。
/// </summary>
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 = 0;
s_PeakInUse = 0;
s_CurrentInUse = 0;
s_IdleFrames = 0;
s_RecentAcquireCount = 0;
s_Stack = Array.Empty<T>();
}
public static int UnusedCount
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => s_Count;
}
internal static MemoryPoolInfo GetInfo()
{
return new MemoryPoolInfo(
typeof(T), s_Count,
s_AcquireCount - s_ReleaseCount,
s_AcquireCount, s_ReleaseCount,
s_CreateCount,
s_HighWaterMark, s_MaxCapacity,
s_IdleFrames, s_Stack.Length);
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: e4ec5f33991c55f41bceefbdb8089ae2
guid: 9db12599664ce4d4eb5d5f581cf97900
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -1,156 +0,0 @@
using System;
using System.Collections.Generic;
namespace AlicizaX
{
public static partial class MemoryPool
{
/// <summary>
/// 内存池收集器。
/// </summary>
private sealed class MemoryCollection
{
private readonly Queue<IMemory> _memories;
private readonly Type _memoryType;
private int _usingMemoryCount;
private int _acquireMemoryCount;
private int _releaseMemoryCount;
private int _addMemoryCount;
private int _removeMemoryCount;
public MemoryCollection(Type memoryType)
{
_memories = new Queue<IMemory>();
_memoryType = memoryType;
_usingMemoryCount = 0;
_acquireMemoryCount = 0;
_releaseMemoryCount = 0;
_addMemoryCount = 0;
_removeMemoryCount = 0;
}
public Type MemoryType => _memoryType;
public int UnusedMemoryCount => _memories.Count;
public int UsingMemoryCount => _usingMemoryCount;
public int AcquireMemoryCount => _acquireMemoryCount;
public int ReleaseMemoryCount => _releaseMemoryCount;
public int AddMemoryCount => _addMemoryCount;
public int RemoveMemoryCount => _removeMemoryCount;
public T Acquire<T>() where T : class, IMemory, new()
{
if (typeof(T) != _memoryType)
{
throw new Exception("Type is invalid.");
}
_usingMemoryCount++;
_acquireMemoryCount++;
lock (_memories)
{
if (_memories.Count > 0)
{
return (T)_memories.Dequeue();
}
}
_addMemoryCount++;
return new T();
}
public IMemory Acquire()
{
_usingMemoryCount++;
_acquireMemoryCount++;
lock (_memories)
{
if (_memories.Count > 0)
{
return _memories.Dequeue();
}
}
_addMemoryCount++;
return (IMemory)Activator.CreateInstance(_memoryType);
}
public void Release(IMemory memory)
{
memory.Clear();
lock (_memories)
{
if (_enableStrictCheck && _memories.Contains(memory))
{
throw new Exception("The memory has been released.");
}
_memories.Enqueue(memory);
}
_releaseMemoryCount++;
_usingMemoryCount--;
}
public void Add<T>(int count) where T : class, IMemory, new()
{
if (typeof(T) != _memoryType)
{
throw new Exception("Type is invalid.");
}
lock (_memories)
{
_addMemoryCount += count;
while (count-- > 0)
{
_memories.Enqueue(new T());
}
}
}
public void Add(int count)
{
lock (_memories)
{
_addMemoryCount += count;
while (count-- > 0)
{
_memories.Enqueue((IMemory)Activator.CreateInstance(_memoryType));
}
}
}
public void Remove(int count)
{
lock (_memories)
{
if (count > _memories.Count)
{
count = _memories.Count;
}
_removeMemoryCount += count;
while (count-- > 0)
{
_memories.Dequeue();
}
}
}
public void RemoveAll()
{
lock (_memories)
{
_removeMemoryCount += _memories.Count;
_memories.Clear();
}
}
}
}
}

View File

@ -1,15 +1,13 @@
using System;
using System.Collections.Generic;
using System;
namespace AlicizaX
{
/// <summary>
/// 内存池。
/// 内存池。保留旧 API 签名,内部转发到 MemoryPool&lt;T&gt; / MemoryPoolRegistry。
/// </summary>
public static partial class MemoryPool
{
private static readonly Dictionary<Type, MemoryCollection> _memoryCollections = new Dictionary<Type, MemoryCollection>();
private static bool _enableStrictCheck = false;
private static bool _enableStrictCheck;
/// <summary>
/// 获取或设置是否开启强制检查。
@ -23,28 +21,14 @@ namespace AlicizaX
/// <summary>
/// 获取内存池的数量。
/// </summary>
// ReSharper disable once InconsistentlySynchronizedField
public static int Count => _memoryCollections.Count;
public static int Count => MemoryPoolRegistry.Count;
/// <summary>
/// 获取所有内存池的信息。
/// </summary>
/// <returns>所有内存池的信息。</returns>
public static MemoryPoolInfo[] GetAllMemoryPoolInfos()
{
int index = 0;
MemoryPoolInfo[] results = null;
lock (_memoryCollections)
{
results = new MemoryPoolInfo[_memoryCollections.Count];
foreach (KeyValuePair<Type, MemoryCollection> memoryCollection in _memoryCollections)
{
results[index++] = new MemoryPoolInfo(memoryCollection.Key, memoryCollection.Value.UnusedMemoryCount, memoryCollection.Value.UsingMemoryCount, memoryCollection.Value.AcquireMemoryCount, memoryCollection.Value.ReleaseMemoryCount, memoryCollection.Value.AddMemoryCount, memoryCollection.Value.RemoveMemoryCount);
}
}
return results;
return MemoryPoolRegistry.GetAllInfos();
}
/// <summary>
@ -52,156 +36,80 @@ namespace AlicizaX
/// </summary>
public static void ClearAll()
{
lock (_memoryCollections)
{
foreach (KeyValuePair<Type, MemoryCollection> memoryCollection in _memoryCollections)
{
memoryCollection.Value.RemoveAll();
}
_memoryCollections.Clear();
}
MemoryPoolRegistry.ClearAll();
}
/// <summary>
/// 从内存池获取内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <returns>内存对象。</returns>
public static T Acquire<T>() where T : class, IMemory, new()
{
return GetMemoryCollection(typeof(T)).Acquire<T>();
return MemoryPool<T>.Acquire();
}
/// <summary>
/// 从内存池获取内存对象。
/// </summary>
/// <param name="memoryType">内存对象类型。</param>
/// <returns>内存对象。</returns>
public static IMemory Acquire(Type memoryType)
{
InternalCheckMemoryType(memoryType);
return GetMemoryCollection(memoryType).Acquire();
return MemoryPoolRegistry.Acquire(memoryType);
}
/// <summary>
/// 将内存对象归还内存池。
/// </summary>
/// <param name="memory">内存对象。</param>
public static void Release(IMemory memory)
{
if (memory == null)
{
throw new Exception("Memory is invalid.");
}
Type memoryType = memory.GetType();
InternalCheckMemoryType(memoryType);
GetMemoryCollection(memoryType).Release(memory);
MemoryPoolRegistry.Release(memory);
}
/// <summary>
/// 向内存池中追加指定数量的内存对象。
/// 向内存池中预热指定数量的内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <param name="count">追加数量。</param>
public static void Add<T>(int count) where T : class, IMemory, new()
{
GetMemoryCollection(typeof(T)).Add<T>(count);
MemoryPool<T>.Prewarm(count);
}
/// <summary>
/// 向内存池中追加指定数量的内存对象。
/// 向内存池中预热指定数量的内存对象。
/// </summary>
/// <param name="memoryType">内存对象类型。</param>
/// <param name="count">追加数量。</param>
public static void Add(Type memoryType, int count)
{
InternalCheckMemoryType(memoryType);
GetMemoryCollection(memoryType).Add(count);
MemoryPoolRegistry.Prewarm(memoryType, count);
}
/// <summary>
/// 从内存池中移除指定数量的内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <param name="count">移除数量。</param>
public static void Remove<T>(int count) where T : class, IMemory
public static void Remove<T>(int count) where T : class, IMemory, new()
{
GetMemoryCollection(typeof(T)).Remove(count);
int target = MemoryPool<T>.UnusedCount - count;
MemoryPool<T>.Shrink(target);
}
/// <summary>
/// 从内存池中移除指定数量的内存对象。
/// </summary>
/// <param name="memoryType">内存对象类型。</param>
/// <param name="count">移除数量。</param>
public static void Remove(Type memoryType, int count)
{
InternalCheckMemoryType(memoryType);
GetMemoryCollection(memoryType).Remove(count);
MemoryPoolRegistry.RemoveFromType(memoryType, count);
}
/// <summary>
/// 从内存池中移除所有的内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
public static void RemoveAll<T>() where T : class, IMemory
public static void RemoveAll<T>() where T : class, IMemory, new()
{
GetMemoryCollection(typeof(T)).RemoveAll();
MemoryPool<T>.ClearAll();
}
/// <summary>
/// 从内存池中移除所有的内存对象。
/// </summary>
/// <param name="memoryType">内存对象类型。</param>
public static void RemoveAll(Type memoryType)
{
InternalCheckMemoryType(memoryType);
GetMemoryCollection(memoryType).RemoveAll();
}
private static void InternalCheckMemoryType(Type memoryType)
{
if (!_enableStrictCheck)
{
return;
}
if (memoryType == null)
{
throw new Exception("Memory type is invalid.");
}
if (!memoryType.IsClass || memoryType.IsAbstract)
{
throw new Exception("Memory type is not a non-abstract class type.");
}
if (!typeof(IMemory).IsAssignableFrom(memoryType))
{
throw new Exception(string.Format("Memory type '{0}' is invalid.", memoryType.FullName));
}
}
private static MemoryCollection GetMemoryCollection(Type memoryType)
{
if (memoryType == null)
{
throw new Exception("MemoryType is invalid.");
}
MemoryCollection memoryCollection = null;
lock (_memoryCollections)
{
if (!_memoryCollections.TryGetValue(memoryType, out memoryCollection))
{
memoryCollection = new MemoryCollection(memoryType);
_memoryCollections.Add(memoryType, memoryCollection);
}
}
return memoryCollection;
MemoryPoolRegistry.ClearType(memoryType);
}
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
namespace AlicizaX
{
@ -30,11 +30,9 @@ namespace AlicizaX
/// <summary>
/// 从内存池获取内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <returns>内存对象。</returns>
public static T Alloc<T>() where T : MemoryObject, new()
{
T memory = Acquire<T>();
T memory = MemoryPool<T>.Acquire();
memory.InitFromPool();
return memory;
}
@ -42,16 +40,15 @@ namespace AlicizaX
/// <summary>
/// 将内存对象归还内存池。
/// </summary>
/// <param name="memory">内存对象。</param>
public static void Dealloc(MemoryObject memory)
{
if (memory == null)
{
throw new Exception("Memory is invalid.");
throw new ArgumentNullException(nameof(memory));
}
memory.RecycleToPool();
Release(memory);
MemoryPoolRegistry.Release(memory);
}
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Runtime.InteropServices;
namespace AlicizaX
@ -10,109 +10,81 @@ namespace AlicizaX
public struct MemoryPoolInfo
{
private readonly Type _type;
private readonly int _unusedMemoryCount;
private readonly int _usingMemoryCount;
private readonly int _acquireMemoryCount;
private readonly int _releaseMemoryCount;
private readonly int _addMemoryCount;
private readonly int _removeMemoryCount;
private readonly int _unusedCount;
private readonly int _usingCount;
private readonly int _acquireCount;
private readonly int _releaseCount;
private readonly int _createCount;
private readonly int _highWaterMark;
private readonly int _maxCapacity;
private readonly int _idleFrames;
private readonly int _poolArrayLength;
/// <summary>
/// 初始化内存池信息的新实例。
/// </summary>
/// <param name="type">内存池类型。</param>
/// <param name="unusedMemoryCount">未使用内存对象数量。</param>
/// <param name="usingMemoryCount">正在使用内存对象数量。</param>
/// <param name="acquireMemoryCount">获取内存对象数量。</param>
/// <param name="releaseMemoryCount">归还内存对象数量。</param>
/// <param name="addMemoryCount">增加内存对象数量。</param>
/// <param name="removeMemoryCount">移除内存对象数量。</param>
public MemoryPoolInfo(Type type, int unusedMemoryCount, int usingMemoryCount, int acquireMemoryCount, int releaseMemoryCount, int addMemoryCount, int removeMemoryCount)
public MemoryPoolInfo(Type type, int unusedCount, int usingCount,
int acquireCount, int releaseCount, int createCount,
int highWaterMark, int maxCapacity,
int idleFrames, int poolArrayLength)
{
_type = type;
_unusedMemoryCount = unusedMemoryCount;
_usingMemoryCount = usingMemoryCount;
_acquireMemoryCount = acquireMemoryCount;
_releaseMemoryCount = releaseMemoryCount;
_addMemoryCount = addMemoryCount;
_removeMemoryCount = removeMemoryCount;
_unusedCount = unusedCount;
_usingCount = usingCount;
_acquireCount = acquireCount;
_releaseCount = releaseCount;
_createCount = createCount;
_highWaterMark = highWaterMark;
_maxCapacity = maxCapacity;
_idleFrames = idleFrames;
_poolArrayLength = poolArrayLength;
}
/// <summary>
/// 获取内存池类型。
/// 池类型。
/// </summary>
public Type Type
{
get
{
return _type;
}
}
public Type Type => _type;
/// <summary>
/// 获取未使用内存对象数量
/// 池中空闲对象数量(可立即借出)。
/// </summary>
public int UnusedMemoryCount
{
get
{
return _unusedMemoryCount;
}
}
public int UnusedCount => _unusedCount;
/// <summary>
/// 获取正在使用内存对象数量。
/// 当前被借出、尚未归还的对象数量。
/// </summary>
public int UsingMemoryCount
{
get
{
return _usingMemoryCount;
}
}
public int UsingCount => _usingCount;
/// <summary>
/// 获取获取内存对象数量
/// 累计 Acquire 调用次数(仅开发模式有效)。
/// </summary>
public int AcquireMemoryCount
{
get
{
return _acquireMemoryCount;
}
}
public int AcquireCount => _acquireCount;
/// <summary>
/// 获取归还内存对象数量
/// 累计 Release 调用次数(仅开发模式有效)。
/// </summary>
public int ReleaseMemoryCount
{
get
{
return _releaseMemoryCount;
}
}
public int ReleaseCount => _releaseCount;
/// <summary>
/// 获取增加内存对象数量
/// 累计 new T() 创建次数(池不够时的实际分配,仅开发模式有效)。
/// </summary>
public int AddMemoryCount
{
get
{
return _addMemoryCount;
}
}
public int CreateCount => _createCount;
/// <summary>
/// 获取移除内存对象数量
/// 近期峰值并发使用量。回收策略据此决定保留多少对象。
/// </summary>
public int RemoveMemoryCount
{
get
{
return _removeMemoryCount;
}
}
public int HighWaterMark => _highWaterMark;
/// <summary>
/// 池容量硬上限。超出后 Release 的对象直接丢弃交给 GC。
/// </summary>
public int MaxCapacity => _maxCapacity;
/// <summary>
/// 连续无 Acquire 的帧数。>=300 开始温和回收,>=900 激进回收+高水位衰减。
/// </summary>
public int IdleFrames => _idleFrames;
/// <summary>
/// 底层 T[] 数组的实际长度。反映真实内存占用(含空槽)。
/// </summary>
public int PoolArrayLength => _poolArrayLength;
}
}

View File

@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace AlicizaX
{
/// <summary>
/// 内存池注册表。非泛型路径通过此表查找对应的泛型池。
/// 注册发生在 MemoryPool&lt;T&gt; 静态构造器中,仅一次。
/// </summary>
public static class MemoryPoolRegistry
{
internal sealed class MemoryPoolHandle
{
public readonly Func<IMemory> Acquire;
public readonly Action<IMemory> Release;
public readonly Action Clear;
public readonly Action<int> Prewarm;
public readonly Func<MemoryPoolInfo> GetInfo;
public readonly Action<int> Tick;
public readonly Action<int> Shrink;
public MemoryPoolHandle(
Func<IMemory> acquire,
Action<IMemory> release,
Action clear,
Action<int> prewarm,
Func<MemoryPoolInfo> getInfo,
Action<int> tick,
Action<int> shrink)
{
Acquire = acquire;
Release = release;
Clear = clear;
Prewarm = prewarm;
GetInfo = getInfo;
Tick = tick;
Shrink = shrink;
}
}
private static readonly Dictionary<Type, MemoryPoolHandle> s_Handles
= new Dictionary<Type, MemoryPoolHandle>(64);
// Tick 回调用数组缓存,避免每帧遍历 Dictionary
private static Action<int>[] s_TickArray = Array.Empty<Action<int>>();
private static int s_TickCount;
private static bool s_TickArrayDirty;
public static int Count => s_Handles.Count;
internal static void Register(Type type, MemoryPoolHandle handle)
{
s_Handles[type] = handle;
s_TickArrayDirty = true;
}
/// <summary>
/// 非泛型 Acquire用于只有 Type 没有泛型参数的场景。
/// </summary>
public static IMemory Acquire(Type type)
{
if (s_Handles.TryGetValue(type, out var handle))
return handle.Acquire();
// 首次访问:触发 MemoryPool<T> 静态构造器
RuntimeHelpers.RunClassConstructor(
typeof(MemoryPool<>).MakeGenericType(type).TypeHandle);
if (s_Handles.TryGetValue(type, out handle))
return handle.Acquire();
throw new Exception($"MemoryPool: Type '{type.FullName}' is not a valid IMemory type.");
}
public static void Release(IMemory memory)
{
if (memory == null)
throw new ArgumentNullException(nameof(memory));
Type type = memory.GetType();
if (s_Handles.TryGetValue(type, out var handle))
{
handle.Release(memory);
return;
}
// 首次访问:触发静态构造器
RuntimeHelpers.RunClassConstructor(
typeof(MemoryPool<>).MakeGenericType(type).TypeHandle);
if (s_Handles.TryGetValue(type, out handle))
handle.Release(memory);
}
public static MemoryPoolInfo[] GetAllInfos()
{
var infos = new MemoryPoolInfo[s_Handles.Count];
int i = 0;
foreach (var kv in s_Handles)
infos[i++] = kv.Value.GetInfo();
return infos;
}
public static void ClearAll()
{
foreach (var kv in s_Handles)
kv.Value.Clear();
}
public static void Prewarm(Type type, int count)
{
if (s_Handles.TryGetValue(type, out var handle))
{
handle.Prewarm(count);
return;
}
RuntimeHelpers.RunClassConstructor(
typeof(MemoryPool<>).MakeGenericType(type).TypeHandle);
if (s_Handles.TryGetValue(type, out handle))
handle.Prewarm(count);
}
public static void ClearType(Type type)
{
if (s_Handles.TryGetValue(type, out var handle))
handle.Clear();
}
public static void RemoveFromType(Type type, int count)
{
if (s_Handles.TryGetValue(type, out var handle))
{
int unused = handle.GetInfo().UnusedCount;
handle.Shrink(unused - count);
}
}
/// <summary>
/// 每帧由 MemoryPoolTicker 调用,驱动所有池的自动回收。
/// </summary>
public static void TickAll(int frameCount)
{
if (s_TickArrayDirty)
RebuildTickArray();
for (int i = 0; i < s_TickCount; i++)
s_TickArray[i](frameCount);
}
private static void RebuildTickArray()
{
s_TickCount = s_Handles.Count;
if (s_TickArray.Length < s_TickCount)
s_TickArray = new Action<int>[s_TickCount];
int i = 0;
foreach (var kv in s_Handles)
s_TickArray[i++] = kv.Value.Tick;
s_TickArrayDirty = false;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e39b8f96e3d484a40bf40d2517b4de02
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -74,5 +74,11 @@ namespace AlicizaX
break;
}
}
private void Update()
{
MemoryPoolRegistry.TickAll(Time.frameCount);
}
}
}

View File

@ -17,50 +17,72 @@ namespace AlicizaX.Debugger.Runtime
protected override void BuildWindow(VisualElement root)
{
VisualElement overview = CreateSection("Reference Pool Overview", out VisualElement overviewCard);
float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f;
// ---- Overview Section ----
VisualElement overview = CreateSection("Memory Pool Overview", out VisualElement overviewCard);
overviewCard.Add(CreateRow("Enable Strict Check", MemoryPool.EnableStrictCheck.ToString()));
overviewCard.Add(CreateRow("Reference Pool Count", MemoryPool.Count.ToString()));
showFullClassNameToggle = ScrollableDebuggerWindowBase.CreateConsoleFilterToggle("Show Full ClassName", m_ShowFullClassName, DebuggerTheme.PrimaryText, value => m_ShowFullClassName = value);
overviewCard.Add(CreateRow("Pool Type Count", MemoryPool.Count.ToString()));
// 统计总缓存对象数和总数组容量
MemoryPoolInfo[] allInfos = MemoryPool.GetAllMemoryPoolInfos();
int totalUnused = 0;
int totalUsing = 0;
int totalArrayLen = 0;
foreach (var info in allInfos)
{
totalUnused += info.UnusedCount;
totalUsing += info.UsingCount;
totalArrayLen += info.PoolArrayLength;
}
overviewCard.Add(CreateRow("Total Cached Objects", totalUnused.ToString()));
overviewCard.Add(CreateRow("Total In Use", totalUsing.ToString()));
overviewCard.Add(CreateRow("Total Array Capacity", totalArrayLen.ToString()));
showFullClassNameToggle = CreateConsoleFilterToggle("Show Full ClassName", m_ShowFullClassName, DebuggerTheme.PrimaryText, value => m_ShowFullClassName = value);
overviewCard.Add(showFullClassNameToggle);
// ---- 操作按钮 ----
VisualElement buttonRow = new VisualElement();
buttonRow.style.flexDirection = FlexDirection.Row;
buttonRow.style.marginTop = 8f * scale;
buttonRow.Add(CreateActionButton("Clear All Pools", () =>
{
MemoryPoolRegistry.ClearAll();
Rebuild();
}, DebuggerTheme.Danger));
overviewCard.Add(buttonRow);
root.Add(overview);
// ---- 按 Assembly 分组 ----
m_ReferencePoolInfos.Clear();
MemoryPoolInfo[] referencePoolInfos = MemoryPool.GetAllMemoryPoolInfos();
foreach (MemoryPoolInfo referencePoolInfo in referencePoolInfos)
foreach (MemoryPoolInfo info in allInfos)
{
string assemblyName = referencePoolInfo.Type.Assembly.GetName().Name;
string assemblyName = info.Type.Assembly.GetName().Name;
if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out List<MemoryPoolInfo> results))
{
results = new List<MemoryPoolInfo>();
m_ReferencePoolInfos.Add(assemblyName, results);
}
results.Add(referencePoolInfo);
results.Add(info);
}
foreach (KeyValuePair<string, List<MemoryPoolInfo>> assemblyReferencePoolInfo in m_ReferencePoolInfos)
foreach (KeyValuePair<string, List<MemoryPoolInfo>> assemblyInfo in m_ReferencePoolInfos)
{
assemblyReferencePoolInfo.Value.Sort(m_ShowFullClassName ? m_FullClassNameComparer : m_NormalClassNameComparer);
VisualElement section = CreateSection(Utility.Text.Format("Assembly: {0}", assemblyReferencePoolInfo.Key), out VisualElement card);
if (assemblyReferencePoolInfo.Value.Count <= 0)
assemblyInfo.Value.Sort(m_ShowFullClassName ? m_FullClassNameComparer : m_NormalClassNameComparer);
VisualElement section = CreateSection(Utility.Text.Format("Assembly: {0}", assemblyInfo.Key), out VisualElement card);
if (assemblyInfo.Value.Count <= 0)
{
card.Add(CreateRow("State", "Reference Pool is Empty ..."));
card.Add(CreateRow("State", "Memory Pool is Empty ..."));
}
else
{
for (int i = 0; i < assemblyReferencePoolInfo.Value.Count; i++)
for (int i = 0; i < assemblyInfo.Value.Count; i++)
{
MemoryPoolInfo info = assemblyReferencePoolInfo.Value[i];
string title = m_ShowFullClassName ? info.Type.FullName : info.Type.Name;
string content = Utility.Text.Format(
"Unused {0} | Using {1} | Acquire {2} | Release {3} | Add {4} | Remove {5}",
info.UnusedMemoryCount,
info.UsingMemoryCount,
info.AcquireMemoryCount,
info.ReleaseMemoryCount,
info.AddMemoryCount,
info.RemoveMemoryCount);
card.Add(CreatePoolInfoItem(title, content));
card.Add(CreatePoolInfoItem(assemblyInfo.Value[i], m_ShowFullClassName, scale));
}
}
@ -78,27 +100,52 @@ namespace AlicizaX.Debugger.Runtime
return a.Type.FullName.CompareTo(b.Type.FullName);
}
private static VisualElement CreatePoolInfoItem(string title, string content)
private static VisualElement CreatePoolInfoItem(MemoryPoolInfo info, bool showFullName, float scale)
{
float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f;
VisualElement item = CreateCard();
item.style.marginBottom = 8f * scale;
item.style.backgroundColor = DebuggerTheme.PanelSurfaceAlt;
string title = showFullName ? info.Type.FullName : info.Type.Name;
Label titleLabel = new Label(title ?? string.Empty);
titleLabel.style.color = DebuggerTheme.PrimaryText;
titleLabel.style.fontSize = 16f * scale;
titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
titleLabel.style.whiteSpace = WhiteSpace.Normal;
titleLabel.style.marginBottom = 6f * scale;
Label contentLabel = new Label(content ?? string.Empty);
contentLabel.style.color = DebuggerTheme.SecondaryText;
contentLabel.style.fontSize = 14f * scale;
contentLabel.style.whiteSpace = WhiteSpace.Normal;
item.Add(titleLabel);
item.Add(contentLabel);
// 基础统计
string stats = Utility.Text.Format(
"Unused {0} | Using {1} | Acquire {2} | Release {3} | Created {4}",
info.UnusedCount, info.UsingCount,
info.AcquireCount, info.ReleaseCount,
info.CreateCount);
Label statsLabel = new Label(stats);
statsLabel.style.color = DebuggerTheme.SecondaryText;
statsLabel.style.fontSize = 14f * scale;
statsLabel.style.whiteSpace = WhiteSpace.Normal;
statsLabel.style.marginBottom = 4f * scale;
item.Add(statsLabel);
// 回收策略状态
string recycleStatus = Utility.Text.Format(
"HighWater {0} | MaxCap {1} | Idle {2}f | Array {3}",
info.HighWaterMark, info.MaxCapacity,
info.IdleFrames, info.PoolArrayLength);
Label recycleLabel = new Label(recycleStatus);
recycleLabel.style.fontSize = 13f * scale;
recycleLabel.style.whiteSpace = WhiteSpace.Normal;
// 根据空闲帧数着色:接近回收阈值时变色
if (info.IdleFrames >= 300)
recycleLabel.style.color = DebuggerTheme.Warning;
else if (info.IdleFrames >= 200)
recycleLabel.style.color = new Color(0.9f, 0.7f, 0.3f);
else
recycleLabel.style.color = DebuggerTheme.SecondaryText;
item.Add(recycleLabel);
return item;
}
}