https://edu.uwa4d.com/course-intro/0/165?purchased=true读书笔记
减少对象数量
内存对齐
在网络发送数据时使用共用体并缓存对象
// StructLayout使设计者可以控制类或结构的数据字段的物理布局
// Explicit与FieldOffset一起可以控制每个数据成员的精确位置
[StructLayout(LayoutKind.Explicit)]
public class CSDataC
{
/ public members /
// FieldOffset控制字段所在的物理位置偏移为0
[FieldOffset(0)]
public CSHeartBeatC stHeartBeat; // 心跳请求,字段无意义,占位用
[FieldOffset(0)]
public CSGM stGM; // Gm的字符串
利用union可以用相同的存储空间存储不同型别的数据类型,从而节省内存空间
发送时还可以缓存,防止new对象
public CSDataC GetCachedSendData(CS_CMD cmd)
{
int key = (int)cmd;
CSDataC ret;
if (!CachedDataCs.TryGetValue(key, out ret))
{
ret = new CSDataC();
ret.@select((long)cmd);
CachedDataCs.Add(key, ret);
}
return ret;
}
使用泛型优化装箱
//一个带泛型类参数委托
public delegate void OnNotificationDelegate(EventData note);
public class EventData
{
}
public class EventDataEx<T> : EventData
{
private T mData;
public EventDataEx(T varData)
{
mData = varData;
}
public T GetData()
{
return mData;
}
}
//监听者解包
void OnEvBuildHomeTown(EventData data)
{
var exdata = data as EventDataEx<bool>;
bool isBuild = exdata.GetData();
}
//发送者
EventManager.Instance.DispatchEvent(Common.EventStr.BuildHomeTown, new EventDataEx<bool>(false));
不要使用可变参数,会产生临时数组
List使用时先确定长度,避免反复扩容new数组
C#对象池
internal static class ListPool<T>
{
// Object pool to avoid allocations.
private static readonly ObjectPool<List<T>> s_ListPool = new ObjectPool<List<T>>(null, l => l.Clear());
public static List<T> Get()
{
return s_ListPool.Get();
}
public static void Release(List<T> toRelease)
{
s_ListPool.Release(toRelease);
}
}
internal class ObjectPool<T> where T : new()
{
private readonly Stack<T> m_Stack = new Stack<T>();
private readonly UnityAction<T> m_ActionOnGet;
private readonly UnityAction<T> m_ActionOnRelease;
public int countAll { get; private set; }
public int countActive { get { return countAll - countInactive; } }
public int countInactive { get { return m_Stack.Count; } }
public ObjectPool(UnityAction<T> actionOnGet = null, UnityAction<T> actionOnRelease = null)
{
m_ActionOnGet = actionOnGet;
m_ActionOnRelease = actionOnRelease;
}
public T Get()
{
T element;
if (m_Stack.Count == 0)
{
element = new T();
countAll++;
}
else
{
element = m_Stack.Pop();
}
if (m_ActionOnGet != null)
m_ActionOnGet(element);
return element;
}
public void Release(T element)
{
if (m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element))
HXLOG.Error("Internal error. Trying to destroy object that is already released to pool.");
if (m_ActionOnRelease != null)
m_ActionOnRelease(element);
m_Stack.Push(element);
}
}
LinkedList
链表LinkedList具有泛型的特点,元素不连续分配,每个元素都有记录前后节点。
优点
插入、删除元素效率比较高。
缺点
不能进行下标索引访问,找元素只能遍历查找不方便
访问效率低
字符串使用StringBuilder
StringBuilder集成工具
https://blog.csdn.net/onelei1994/article/details/101054633
using System.Text;
using UnityEngine;
/// <summary>
/// 字符串优化类
/// </summary>
public class QString
{
private static StringBuilder stringBuilder = new StringBuilder();
private static StringBuilder shareStringBuilder = new StringBuilder();
public static StringBuilder GetShareStringBuilder()
{
shareStringBuilder.Remove(0, stringBuilder.Length);
return shareStringBuilder;
}
public static string Format(string src, params object[] args)
{
stringBuilder.Remove(0, stringBuilder.Length);
stringBuilder.AppendFormat(src, args);
return stringBuilder.ToString();
}
public static string Concat(string s1, string s2)
{
stringBuilder.Remove(0, stringBuilder.Length);
stringBuilder.Append(s1);
stringBuilder.Append(s2);
return stringBuilder.ToString();
}
public static string Concat(string s1, string s2, string s3)
{
stringBuilder.Remove(0, stringBuilder.Length);
stringBuilder.Append(s1);
stringBuilder.Append(s2);
stringBuilder.Append(s3);
return stringBuilder.ToString();
}
}
public class QStringSample
{
public void Test()
{
StringBuilder stringBuilder = QString.GetShareStringBuilder();
for (int i = 0; i < 100; i++)
{
stringBuilder.Append(i);
}
Debug.Log(stringBuilder.ToString());
}
}
匿名函数,带参数缓存住
https://zhuanlan.zhihu.com/p/25779397
当不使用外部变量的匿名函数时,编译器会把这个函数变成静态函数,在首次调用时初始化,之后就再也不会new新的对象。 当使用外部变量时,每次调用都会生成一个临时action变量,这个就是alloc的原因。
解决办法以及相关建议
这里会麻烦一点,需要声明一下:
public Action<int, int> pCall;
void Start()
{
pCall = CallVariable;
... // 其他初始化代码
}
void FixedCall()
{
table.Forecah(pCall);
}
void CallVariable(int k, int v)
{
count = k + v;
}
协程优化
WaitForSeconds缓存住,防止每次new一个
UnityAPI
object.nameGC
object.name object.tag使用object.CompareTag
UnityAPI里所有返回数组都会产生GC
GetComponentsInChildren(List results);
materials产生GC
Physics.RayCastNonAlloc
UGUI的Text为空时,放个空格
UGUI的GC优化其它文章说的比较详细了,这里说一个比较容易忽视的一点,就是当Prefab中有大量空的Text,初始化的时候会有一个很严重的GC Alloc,这是因为在初始化时,会先初始化TextGenerator,如果Text为空,则会先按50个字来初始化,即50个字的UIVertex和50个字的UICharInfo,这种可以不让它为空,或者填一个空格进去来阻止。
Protobuf优化
反复创建的字节流,使用缓存,主要缓存字节流,和序列化对象,ObjectPool
发送时缓存
msSend.ResetStream();
ntf_battle_frame_data dataTmp = ProtoFactory.Get<ntf_battle_frame_data>();
ProtoBufSerializer.Serialize(msSend.memStream, dataTmp);
ProtoFactory.Recycle(dataTmp);//*************回收,很重要
针对每个池,回收单个对象时,单个对象数据初始化
public sealed class CmdWithFramePool : ProtoPoolBase<ntf_battle_frame_data.cmd_with_frame>
{
protected override void RecycleChildren(ntf_battle_frame_data.cmd_with_frame netData)
{
if (netData.cmd != null)
{
ProtoFactory.Recycle(netData.cmd);
}
}
protected override void ClearNetData(ntf_battle_frame_data.cmd_with_frame netData)
{
netData.server_frame = 0;
netData.cmd = null;
}
}