重构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;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -34,20 +34,44 @@ namespace AlicizaX.Editor
t.EnableStrictCheck = enableStrictCheck; 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_ShowFullClassName = EditorGUILayout.Toggle("Show Full Class Name", m_ShowFullClassName);
m_ReferencePoolInfos.Clear();
// 全局统计
MemoryPoolInfo[] referencePoolInfos = MemoryPool.GetAllMemoryPoolInfos(); 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) foreach (MemoryPoolInfo referencePoolInfo in referencePoolInfos)
{ {
string assemblyName = referencePoolInfo.Type.Assembly.GetName().Name; string assemblyName = referencePoolInfo.Type.Assembly.GetName().Name;
List<MemoryPoolInfo> results = null; if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out List<MemoryPoolInfo> results))
if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out results))
{ {
results = new List<MemoryPoolInfo>(); results = new List<MemoryPoolInfo>();
m_ReferencePoolInfos.Add(assemblyName, results); m_ReferencePoolInfos.Add(assemblyName, results);
} }
results.Add(referencePoolInfo); results.Add(referencePoolInfo);
} }
@ -58,20 +82,16 @@ namespace AlicizaX.Editor
if (currentState != lastState) if (currentState != lastState)
{ {
if (currentState) if (currentState)
{
m_OpenedItems.Add(assemblyReferencePoolInfo.Key); m_OpenedItems.Add(assemblyReferencePoolInfo.Key);
}
else else
{
m_OpenedItems.Remove(assemblyReferencePoolInfo.Key); m_OpenedItems.Remove(assemblyReferencePoolInfo.Key);
}
} }
if (currentState) if (currentState)
{ {
EditorGUILayout.BeginVertical("box"); 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); EditorGUILayout.LabelField(m_ShowFullClassName ? "Full Class Name" : "Class Name", label);
assemblyReferencePoolInfo.Value.Sort(Comparison); assemblyReferencePoolInfo.Value.Sort(Comparison);
foreach (MemoryPoolInfo referencePoolInfo in assemblyReferencePoolInfo.Value) foreach (MemoryPoolInfo referencePoolInfo in assemblyReferencePoolInfo.Value)
@ -81,31 +101,34 @@ namespace AlicizaX.Editor
if (GUILayout.Button("Export CSV Data")) 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)) if (!string.IsNullOrEmpty(exportFileName))
{ {
try try
{ {
int index = 0; int index = 0;
string[] data = new string[assemblyReferencePoolInfo.Value.Count + 1]; string[] data = new string[assemblyReferencePoolInfo.Value.Count + 1];
data[index++] = "Class Name,Full Class Name,Unused,Using,Acquire,Release,Add,Remove"; data[index++] = "Class Name,Full Class Name,Unused,Using,Acquire,Release,Created,HighWaterMark,MaxCapacity,IdleFrames,ArrayLength";
foreach (MemoryPoolInfo referencePoolInfo in assemblyReferencePoolInfo.Value) 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); 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) 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.EndVertical();
EditorGUILayout.Separator(); EditorGUILayout.Separator();
} }
} }
@ -125,26 +148,23 @@ namespace AlicizaX.Editor
m_EnableStrictCheck = serializedObject.FindProperty("m_EnableStrictCheck"); m_EnableStrictCheck = serializedObject.FindProperty("m_EnableStrictCheck");
} }
private void DrawReferencePoolInfo(MemoryPoolInfo referencePoolInfo) private void DrawReferencePoolInfo(MemoryPoolInfo info)
{ {
#if UNITY_6000_0_OR_NEWER string name = m_ShowFullClassName ? info.Type.FullName : info.Type.Name;
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)); string values = Utility.Text.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8}",
#else info.UnusedCount, info.UsingCount,
EditorGUILayout.LabelField(m_ShowFullClassName ? referencePoolInfo.Type.FullName : info.AcquireCount, info.ReleaseCount,
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)); info.CreateCount, info.HighWaterMark,
#endif info.MaxCapacity, info.IdleFrames, info.PoolArrayLength);
EditorGUILayout.LabelField(name, values);
} }
private int Comparison(MemoryPoolInfo a, MemoryPoolInfo b) private int Comparison(MemoryPoolInfo a, MemoryPoolInfo b)
{ {
if (m_ShowFullClassName) if (m_ShowFullClassName)
{
return a.Type.FullName.CompareTo(b.Type.FullName); return a.Type.FullName.CompareTo(b.Type.FullName);
}
else else
{
return a.Type.Name.CompareTo(b.Type.Name); 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 fileFormatVersion: 2
guid: e4ec5f33991c55f41bceefbdb8089ae2 guid: 9db12599664ce4d4eb5d5f581cf97900
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 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;
using System.Collections.Generic;
namespace AlicizaX namespace AlicizaX
{ {
/// <summary> /// <summary>
/// 内存池。 /// 内存池。保留旧 API 签名,内部转发到 MemoryPool&lt;T&gt; / MemoryPoolRegistry。
/// </summary> /// </summary>
public static partial class MemoryPool public static partial class MemoryPool
{ {
private static readonly Dictionary<Type, MemoryCollection> _memoryCollections = new Dictionary<Type, MemoryCollection>(); private static bool _enableStrictCheck;
private static bool _enableStrictCheck = false;
/// <summary> /// <summary>
/// 获取或设置是否开启强制检查。 /// 获取或设置是否开启强制检查。
@ -23,28 +21,14 @@ namespace AlicizaX
/// <summary> /// <summary>
/// 获取内存池的数量。 /// 获取内存池的数量。
/// </summary> /// </summary>
// ReSharper disable once InconsistentlySynchronizedField public static int Count => MemoryPoolRegistry.Count;
public static int Count => _memoryCollections.Count;
/// <summary> /// <summary>
/// 获取所有内存池的信息。 /// 获取所有内存池的信息。
/// </summary> /// </summary>
/// <returns>所有内存池的信息。</returns>
public static MemoryPoolInfo[] GetAllMemoryPoolInfos() public static MemoryPoolInfo[] GetAllMemoryPoolInfos()
{ {
int index = 0; return MemoryPoolRegistry.GetAllInfos();
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;
} }
/// <summary> /// <summary>
@ -52,156 +36,80 @@ namespace AlicizaX
/// </summary> /// </summary>
public static void ClearAll() public static void ClearAll()
{ {
lock (_memoryCollections) MemoryPoolRegistry.ClearAll();
{
foreach (KeyValuePair<Type, MemoryCollection> memoryCollection in _memoryCollections)
{
memoryCollection.Value.RemoveAll();
}
_memoryCollections.Clear();
}
} }
/// <summary> /// <summary>
/// 从内存池获取内存对象。 /// 从内存池获取内存对象。
/// </summary> /// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <returns>内存对象。</returns>
public static T Acquire<T>() where T : class, IMemory, new() public static T Acquire<T>() where T : class, IMemory, new()
{ {
return GetMemoryCollection(typeof(T)).Acquire<T>(); return MemoryPool<T>.Acquire();
} }
/// <summary> /// <summary>
/// 从内存池获取内存对象。 /// 从内存池获取内存对象。
/// </summary> /// </summary>
/// <param name="memoryType">内存对象类型。</param>
/// <returns>内存对象。</returns>
public static IMemory Acquire(Type memoryType) public static IMemory Acquire(Type memoryType)
{ {
InternalCheckMemoryType(memoryType); return MemoryPoolRegistry.Acquire(memoryType);
return GetMemoryCollection(memoryType).Acquire();
} }
/// <summary> /// <summary>
/// 将内存对象归还内存池。 /// 将内存对象归还内存池。
/// </summary> /// </summary>
/// <param name="memory">内存对象。</param>
public static void Release(IMemory memory) public static void Release(IMemory memory)
{ {
if (memory == null) MemoryPoolRegistry.Release(memory);
{
throw new Exception("Memory is invalid.");
}
Type memoryType = memory.GetType();
InternalCheckMemoryType(memoryType);
GetMemoryCollection(memoryType).Release(memory);
} }
/// <summary> /// <summary>
/// 向内存池中追加指定数量的内存对象。 /// 向内存池中预热指定数量的内存对象。
/// </summary> /// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <param name="count">追加数量。</param>
public static void Add<T>(int count) where T : class, IMemory, new() public static void Add<T>(int count) where T : class, IMemory, new()
{ {
GetMemoryCollection(typeof(T)).Add<T>(count); MemoryPool<T>.Prewarm(count);
} }
/// <summary> /// <summary>
/// 向内存池中追加指定数量的内存对象。 /// 向内存池中预热指定数量的内存对象。
/// </summary> /// </summary>
/// <param name="memoryType">内存对象类型。</param>
/// <param name="count">追加数量。</param>
public static void Add(Type memoryType, int count) public static void Add(Type memoryType, int count)
{ {
InternalCheckMemoryType(memoryType); MemoryPoolRegistry.Prewarm(memoryType, count);
GetMemoryCollection(memoryType).Add(count);
} }
/// <summary> /// <summary>
/// 从内存池中移除指定数量的内存对象。 /// 从内存池中移除指定数量的内存对象。
/// </summary> /// </summary>
/// <typeparam name="T">内存对象类型。</typeparam> public static void Remove<T>(int count) where T : class, IMemory, new()
/// <param name="count">移除数量。</param>
public static void Remove<T>(int count) where T : class, IMemory
{ {
GetMemoryCollection(typeof(T)).Remove(count); int target = MemoryPool<T>.UnusedCount - count;
MemoryPool<T>.Shrink(target);
} }
/// <summary> /// <summary>
/// 从内存池中移除指定数量的内存对象。 /// 从内存池中移除指定数量的内存对象。
/// </summary> /// </summary>
/// <param name="memoryType">内存对象类型。</param>
/// <param name="count">移除数量。</param>
public static void Remove(Type memoryType, int count) public static void Remove(Type memoryType, int count)
{ {
InternalCheckMemoryType(memoryType); MemoryPoolRegistry.RemoveFromType(memoryType, count);
GetMemoryCollection(memoryType).Remove(count);
} }
/// <summary> /// <summary>
/// 从内存池中移除所有的内存对象。 /// 从内存池中移除所有的内存对象。
/// </summary> /// </summary>
/// <typeparam name="T">内存对象类型。</typeparam> public static void RemoveAll<T>() where T : class, IMemory, new()
public static void RemoveAll<T>() where T : class, IMemory
{ {
GetMemoryCollection(typeof(T)).RemoveAll(); MemoryPool<T>.ClearAll();
} }
/// <summary> /// <summary>
/// 从内存池中移除所有的内存对象。 /// 从内存池中移除所有的内存对象。
/// </summary> /// </summary>
/// <param name="memoryType">内存对象类型。</param>
public static void RemoveAll(Type memoryType) public static void RemoveAll(Type memoryType)
{ {
InternalCheckMemoryType(memoryType); MemoryPoolRegistry.ClearType(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;
} }
} }
} }

View File

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

View File

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

View File

@ -17,50 +17,72 @@ namespace AlicizaX.Debugger.Runtime
protected override void BuildWindow(VisualElement root) 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("Enable Strict Check", MemoryPool.EnableStrictCheck.ToString()));
overviewCard.Add(CreateRow("Reference Pool Count", MemoryPool.Count.ToString())); overviewCard.Add(CreateRow("Pool Type Count", MemoryPool.Count.ToString()));
showFullClassNameToggle = ScrollableDebuggerWindowBase.CreateConsoleFilterToggle("Show Full ClassName", m_ShowFullClassName, DebuggerTheme.PrimaryText, value => m_ShowFullClassName = value);
// 统计总缓存对象数和总数组容量
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); 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); root.Add(overview);
// ---- 按 Assembly 分组 ----
m_ReferencePoolInfos.Clear(); m_ReferencePoolInfos.Clear();
MemoryPoolInfo[] referencePoolInfos = MemoryPool.GetAllMemoryPoolInfos(); foreach (MemoryPoolInfo info in allInfos)
foreach (MemoryPoolInfo referencePoolInfo in referencePoolInfos)
{ {
string assemblyName = referencePoolInfo.Type.Assembly.GetName().Name; string assemblyName = info.Type.Assembly.GetName().Name;
if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out List<MemoryPoolInfo> results)) if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out List<MemoryPoolInfo> results))
{ {
results = new List<MemoryPoolInfo>(); results = new List<MemoryPoolInfo>();
m_ReferencePoolInfos.Add(assemblyName, results); m_ReferencePoolInfos.Add(assemblyName, results);
} }
results.Add(info);
results.Add(referencePoolInfo);
} }
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); assemblyInfo.Value.Sort(m_ShowFullClassName ? m_FullClassNameComparer : m_NormalClassNameComparer);
VisualElement section = CreateSection(Utility.Text.Format("Assembly: {0}", assemblyReferencePoolInfo.Key), out VisualElement card); VisualElement section = CreateSection(Utility.Text.Format("Assembly: {0}", assemblyInfo.Key), out VisualElement card);
if (assemblyReferencePoolInfo.Value.Count <= 0)
if (assemblyInfo.Value.Count <= 0)
{ {
card.Add(CreateRow("State", "Reference Pool is Empty ...")); card.Add(CreateRow("State", "Memory Pool is Empty ..."));
} }
else else
{ {
for (int i = 0; i < assemblyReferencePoolInfo.Value.Count; i++) for (int i = 0; i < assemblyInfo.Value.Count; i++)
{ {
MemoryPoolInfo info = assemblyReferencePoolInfo.Value[i]; card.Add(CreatePoolInfoItem(assemblyInfo.Value[i], m_ShowFullClassName, scale));
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));
} }
} }
@ -78,27 +100,52 @@ namespace AlicizaX.Debugger.Runtime
return a.Type.FullName.CompareTo(b.Type.FullName); 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(); VisualElement item = CreateCard();
item.style.marginBottom = 8f * scale; item.style.marginBottom = 8f * scale;
item.style.backgroundColor = DebuggerTheme.PanelSurfaceAlt; item.style.backgroundColor = DebuggerTheme.PanelSurfaceAlt;
string title = showFullName ? info.Type.FullName : info.Type.Name;
Label titleLabel = new Label(title ?? string.Empty); Label titleLabel = new Label(title ?? string.Empty);
titleLabel.style.color = DebuggerTheme.PrimaryText; titleLabel.style.color = DebuggerTheme.PrimaryText;
titleLabel.style.fontSize = 16f * scale; titleLabel.style.fontSize = 16f * scale;
titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold; titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
titleLabel.style.whiteSpace = WhiteSpace.Normal; titleLabel.style.whiteSpace = WhiteSpace.Normal;
titleLabel.style.marginBottom = 6f * scale; 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(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; return item;
} }
} }