重构Class内存池
This commit is contained in:
parent
10de7e040f
commit
2ac929c364
@ -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);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
231
Runtime/ABase/Base/MemoryPool/MemoryPool.Core.cs
Normal file
231
Runtime/ABase/Base/MemoryPool/MemoryPool.Core.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: e4ec5f33991c55f41bceefbdb8089ae2
|
guid: 9db12599664ce4d4eb5d5f581cf97900
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace AlicizaX
|
namespace AlicizaX
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 内存池。
|
/// 内存池。保留旧 API 签名,内部转发到 MemoryPool<T> / 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
166
Runtime/ABase/Base/MemoryPool/MemoryPoolRegistry.cs
Normal file
166
Runtime/ABase/Base/MemoryPool/MemoryPoolRegistry.cs
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace AlicizaX
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 内存池注册表。非泛型路径通过此表查找对应的泛型池。
|
||||||
|
/// 注册发生在 MemoryPool<T> 静态构造器中,仅一次。
|
||||||
|
/// </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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Runtime/ABase/Base/MemoryPool/MemoryPoolRegistry.cs.meta
Normal file
11
Runtime/ABase/Base/MemoryPool/MemoryPoolRegistry.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e39b8f96e3d484a40bf40d2517b4de02
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -74,5 +74,11 @@ namespace AlicizaX
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
MemoryPoolRegistry.TickAll(Time.frameCount);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user