Unity GC优化原理及实践-1堆栈


1.示例(堆栈上的内存分配):

public class Point
{
    public float pointX { get; set; }
    public float pointY { get; set; }
}

class StartProgram : MonoBehaviour
{
    void Start()
    {
        float pointX = 100.1f;
        InitialPoint(pointX);
    }

    private void InitPoint(float pointX)
    {
        Point point = new Point();
        point.PointX = pointX;
    }
}

首先将Start()方法指令压入栈底,然后压入局部变量pointX。接着将InitPoint()方法压入栈底,形参pointX压入栈底,在堆上实例化Point对象(包括其成员变量PointX和PointY),并在栈上创建point变量指向堆上的Point对象,最后给成员变量PointX赋值

(1)创建引用类型时,会为其分配两个空间,一块空间分配在堆上,存储引用类型本身的数据,另一个块空间分配在栈上,存储对堆上数据的引用

(2)创建值类型时, 会为其分配一个空间,这个空间分配在变量创建的地方

如果值类型是在方法内部创建,则跟随方法入栈,分配到栈上存储。

如果值类型是引用类型的成员变量,则跟随引用类型,存储在堆上。

2.堆栈的特点

(1)栈是一种先进后出的内存结构,只能在一端对数据进行操作,也就是栈顶端进行操作

(2)方法的调用追踪

(3)栈也是一种内存自我管理的结构,压栈自动分配内存,出栈自动清空所占内存

(4)栈中的内存不能动态请求,只能为大小确定的数据分配内存,灵活性不高,但是栈的执行效率很高。

(5)栈的可用空间并不大,所以我们在操作分配到栈上的数据时要注意数据的大小带来的影响。

(1)堆在C#中用于存储实实例对象,能存储大量数据,而且堆能够动态分配存储空间。

(2)相比栈只能在一端操作,堆中的数据可以随意存取。容易产生内存碎片

(3)堆的结构使得堆的执行效率不如栈高,不能自动回收使用过的对象。Unity和lua使用的GC器是标记-清除。

3.数据传递的三种类型

(1)按值传递

public struct PointStruct
{
    public float pointX { get; set; }
    public float pointY { get; set; }
}

public class PointClass
{
    public float pointX { get; set; }
    public float pointY { get; set; }
}

void Start()
{
    PointStruct pointStruct1 = new PointStruct();
    PointClass pointClass1 = new PointClass();
    PointStruct pointStruct2 = pointStruct1;
    PointClass pointClass2 = pointClass1;
}

创建了一个结构体pointStruct1和一个类实例pointClass1, 结合上面的内存分配规则,对于pointSturct1,会在栈上分配内存存储其数据本身,对于pointClass1,会在堆上分配内存存储实例,且在栈上存储指向实例的引用

(2) 参数传递

void Start()
{
    float pointX1 = 100.1f;
    Point point1 = new Point();
    point1.pointX = 200.1f;
    InitialPoint(pointX1, point1);
    Debug.Log(string.Format("pointX1:{0}", pointX1));
    Debug.Log (string.Format("point1.PointX:{0}", point1.PointX));
}

void InitPoint(float pointX2, Point point2)
{
    pointX2 = 300.1f;
    point2.pointX = pointX2;
}

void InitPoint(float pointX2, Point point2)
{
    pointX2 = 300.1f;
    point2.pointX = pointX2;
    point2 = null;
}

point2设置为null的含义,并不将堆上的实例变为null,而是设置栈上的引用为null

(3) 按引用传递(Ref和Out关键字)

void Start()
{
    float pointX1 = 100.1f;
    Point point1 = new Point();
    point1.pointX = 200.1f;
    Point point2 = point1;
    InitPoint(ref pointX1, ref point1);
    Debug.LogWarning(string.Format("pointX1:{0}", pointX1));
    if (point1 != null)
        Debug.LogWarning(string.Format("point1.pointX:{0}", point1.pointX));
    else
        Debug.LogWarning(string.Format("point1 is null"));

    if (point2 != null)
        Debug.LogWarning(string.Format("point2.pointX:{0}", point2.pointX));
    else
        Debug.LogWarning(string.Format("point2 is null"));
}

void InitPoint(ref float pointX2, ref Point point3)
{
    pointX2 = 300.1f;
    point3.pointX = pointX2;
    point3 = null;
}

避坑