TEngine--MemoryPool模块

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
/// <summary>
/// 日志记录结点。
/// </summary>
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;

/// <summary>
/// 初始化日志记录结点的新实例。
/// </summary>
public LogNode()
{
m_LogTime = default(DateTime);
m_LogFrameCount = 0;
m_LogType = LogType.Error;
m_LogMessage = null;
m_StackTrack = null;
}

/// <summary>
/// 获取日志时间。
/// </summary>
public DateTime LogTime
{
get
{
return m_LogTime;
}
}

/// <summary>
/// 获取日志帧计数。
/// </summary>
public int LogFrameCount
{
get
{
return m_LogFrameCount;
}
}

/// <summary>
/// 获取日志类型。
/// </summary>
public LogType LogType
{
get
{
return m_LogType;
}
}

/// <summary>
/// 获取日志内容。
/// </summary>
public string LogMessage
{
get
{
return m_LogMessage;
}
}

/// <summary>
/// 获取日志堆栈信息。
/// </summary>
public string StackTrack
{
get
{
return m_StackTrack;
}
}

/// <summary>
/// 创建日志记录结点。
/// </summary>
/// <param name="logType">日志类型。</param>
/// <param name="logMessage">日志内容。</param>
/// <param name="stackTrack">日志堆栈信息。</param>
/// <returns>创建的日志记录结点。</returns>
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;
}

/// <summary>
/// 清理日志记录结点。
/// </summary>
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
{
/// <summary>
/// 从内存池获取内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <returns>内存对象。</returns>
public static T Alloc<T>() where T : MemoryObject, new()
{
T memory = Acquire<T>();
memory.InitFromPool();
return memory;
}

/// <summary>
/// 将内存对象归还内存池。
/// </summary>
/// <param name="memory">内存对象。</param>
public static void Dealloc(MemoryObject memory)
{
if (memory == null)
{
throw new Exception("Memory is invalid.");
}

memory.RecycleToPool();
Release(memory);
}
}

TEngine--MemoryPool模块
https://www.liu2dream.fun/post/TEngine--MemoryPool模块/
作者
刘老师 MrLiu
发布于
2024年6月14日
许可协议