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; }
避坑