先说一下内存和堆栈:
当程序运行时,.Net会在两个地方存储程序信息,堆内存和栈内存,它们只是对内存的逻辑分段,在程序运行过程中扮演者不同的角色。
栈内存更多的是负责追踪函数调用,内存堆更多的负责记录数据对象。可以把栈看成是一堆盒子一个压着一个的堆叠在一起,当调用一个函数的时候,会在栈顶堆一个新的盒子,我们只能使用最上面的盒子,当最上面的盒子处理完之后(函数调用完成),我们把它扔掉,然后继续处理当前处于栈顶的盒子。
- 从结构来说,栈是一种后进先出(LIFO)型结构,只能使用栈顶的数据,将栈顶数据弹出之后,才能使用下面的数据;堆是一种列表类型,可以访问堆中的任何数据 。
- 从管理方式来说,栈能够对实现对内存的自我管理;堆得内存管理需要通过GC来实现。
下面就说一下,code 中怎么占用内存的,或者说是什么占用了内存?
值类型 是分配在栈上(stack)
public struct ValPoint {
public int x;
public void Blank() { }
}
ValPoint vPoint1 = new ValPoint();
Console.WriteLine(vPoint1.x); // 输出为0
可能大家以为new 就会分配内存,这里的struct 值类型new的时候是不会分配内存的。他只调用ValPoint 这个结构的默认构造而已。
ValPoint vPoint=new ValPoint(10);
这个时候会是怎么样的呢?
答:这个时候会把10 压入栈中,再通过构造把x 属性赋值。
引用类型 是分配在堆上(heap)
当声明一个引用类型变量,并使用new操作符创建引用类型实例的时候,该引用类型的 变量会被分配到线程栈上,变量保存了位于堆上的引用类型的实例的内存地址。变量本身不包含任何类型所定义的数据。
如果仅仅声明一个变量,但不使用new操作符,由于在堆上还没有创建类型的实例,因此,变量值为null,意思是不指向任何对象(堆上的对象的实例)。对于变量的类型声明,用于限制此变量可以保存的类型实例的地址。
例子:假设有以下对象
public class RefPoint {
public int x;
public RefPoint(int x) {this.x = x;}
public RefPoint() {}
}
RefPoint rPoint1;//这样声明不会分配内存
在线程栈上创建一个不包含任何数据,也不指向任何对象 (不包含内存地址)的变量。
RefPoint rPoint1;//这样声明不会分配内存
rPoint1= new RefPoint(1);//加上这一句就不一样了
在应用程序堆(Heap)上创建一个引用类型(RefPoint)对象的实例,并为它分配内存地址。
自动传递该实例的引用给构造函数。(正因为如此,才可以在构造函数中使用this来 访问这个实例。)
调用该类型的构造函数
返回该实例的引用(内存地址),赋值给rPoint1变量;
这样就完成了实例化,这下我们就知道实例化过程中是怎么占用内存的了。