本文最后更新于 2024-06-15T17:54:06+08:00
MemoryPool模块
由于TE作者写的内存池文章内容偏少,所以就不贴链接了,直接看本文就好了。
内存池在框架内的应用
内存池又叫引用池,顾名思义内存池(引用池)的作用就是用来方便建立引用和释放内存的。我们来看一下框架内部内存池(引用池)的引用案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
|
public sealed class LogNode : IMemory { private DateTime m_LogTime; private int m_LogFrameCount; private LogType m_LogType; private string m_LogMessage; private string m_StackTrack;
public LogNode() { m_LogTime = default(DateTime); m_LogFrameCount = 0; m_LogType = LogType.Error; m_LogMessage = null; m_StackTrack = null; }
public DateTime LogTime { get { return m_LogTime; } }
public int LogFrameCount { get { return m_LogFrameCount; } }
public LogType LogType { get { return m_LogType; } }
public string LogMessage { get { return m_LogMessage; } }
public string StackTrack { get { return m_StackTrack; } }
public static LogNode Create(LogType logType, string logMessage, string stackTrack) { LogNode logNode = MemoryPool.Acquire<LogNode>(); logNode.m_LogTime = DateTime.UtcNow; logNode.m_LogFrameCount = Time.frameCount; logNode.m_LogType = logType; logNode.m_LogMessage = logMessage; logNode.m_StackTrack = stackTrack; return logNode; }
public void Clear() { m_LogTime = default(DateTime); m_LogFrameCount = 0; m_LogType = LogType.Error; m_LogMessage = null; m_StackTrack = null; } }
|
其中的重点就是这两个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public static LogNode Create(LogType logType, string logMessage, string stackTrack) { LogNode logNode = MemoryPool.Acquire<LogNode>(); logNode.m_LogTime = DateTime.UtcNow; logNode.m_LogFrameCount = Time.frameCount; logNode.m_LogType = logType; logNode.m_LogMessage = logMessage; logNode.m_StackTrack = stackTrack; return logNode; }
public void Clear() { m_LogTime = default(DateTime); m_LogFrameCount = 0; m_LogType = LogType.Error; m_LogMessage = null; m_StackTrack = null; }
|
在框架内的案例我们可以看到,内存池(引用池)在框架内的使用方法就是在平时需要经常new一个类的时候,用内存池(引用池)的Acquire来创建并赋值,然后再需要清理的时候通过内存池来同一管理,Clear方法里一般是赋默认值,有时候也用dispose等方法,看具体情况。
最后在需要管理内存对象的地方添加MemoryPool里的方法,最常见的就是Release方法了,在Release方法里会调用内存对象类里的clear方法。下面是ConsoleWindow类里的管理LogNode内存对象的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13
| private void OnLogMessageReceived(string logMessage, string stackTrace, LogType logType) { if (logType == LogType.Assert) { logType = LogType.Error; }
_logNodes.Enqueue(LogNode.Create(logType, logMessage, stackTrace)); while (_logNodes.Count > m_MaxLine) { MemoryPool.Release(_logNodes.Dequeue()); } }
|
该方法表示在接收到log消息的时候,先将创建出来的LogNode内存对象入列,然后判断LogNodes队列的数量是否大于m_MaxLine(默认值是100),如果超过该值,就把最先入列的内存对象释放掉,防止无限制的创建LogNode内存对象。
通过代码分析,我们可以发现框架内的debugger模块记录log的最大值默认值是100个。
TEngine的内存池类图

我们来看一下内存池类图,我把内存池的类分为了两部分,左半边主要是内存池管理相关、右半边则是内存池信息和模块。也就是说内存池的核心代码都在左半边。
内存池管理相关
左半边的内存池部分有四个类,我来分别说明一下他们的作用。
1.MemoryObject和IMemory类都是用来创建内存对象的父类,MemoryObject继承了IMemory接口,提供了更多的方法来方便内存对象的创建。
2.MemoryPool是内存池类,用来管理上面说的内存对象的
3.MemoryCollection内存收集类,它是用来收集被Release方法回收过的内存对象,进入未被使用的内存对象队列。如果下次Acquire方法获取对象时有未被使用的内存对象,直接从收集类中取出来。
右半边的内存信息和模块类
内存信息类负责内存信息的统计,内存模块类则负责这些信息的展示与控制的开关。统计信息可以在Memory模块运行时查看。

内存池在框架里的另一种用法
在之前使用里,LogNode类继承了IMemory接口实现了内存池。其实还有一个MemoryObject类,它继承了IMemory接口,而且还提供了其他的方法的接口。我们也可以用这个类来实现内存池对象的创建,MemoryPool类也提供了MemoryObject的相关方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public static partial class MemoryPool { public static T Alloc<T>() where T : MemoryObject, new() { T memory = Acquire<T>(); memory.InitFromPool(); return memory; }
public static void Dealloc(MemoryObject memory) { if (memory == null) { throw new Exception("Memory is invalid."); }
memory.RecycleToPool(); Release(memory); } }
|