我们写一个程序的时候,有什么的逻辑代码,也有其中定义的变量,且根据作用域分为全局变量和局部变量,或分为静态变量,公有变量私有变量,复杂的程序和包括附加其上的贴图和声音等这些资源。那么这些代码和数据在执行的过程中究竟是怎么存放的呢。
计算机语言最终都会被转化为二进制的原材料,然后被加载进计算机的存储空间(主要是内存),以备CPU使用。一般内存可以分为代码区、常量区、静态区、堆、栈几个部分,每个部分存放不同类型的数据。内存可以把他理解为一个大的仓库,当有程序执行需要开辟内存时,就从里面扒一块区域出来然后分成栈堆这些部分,不用了就清空。
程序执行的开始就会在内存区开辟一块新鲜的内存(不一定是连续的),然后在划分成不同的块。上图中:
黄色的部分:是只读区,内存的使用和释放也是由操作系统控制的。
绿色的部分:是可读写的。
栈区:的使用和释放也是操作系统决定的,栈区可以理解为一个注射器,数据进来的时候往下抽一点, 执行完就往上推 一点,内存是及时释放的。
堆和静态区:的在某些语言中(如C,C++)中这部分内存是需要程序员自己去释放的,如果不释放,就会造成内存泄 漏,在又GC机制的语言中,这部分的内存可以由GC托管,程序员就不用操这个心了,比如JAVA,C#,JS。
注意:还有一种数据是完全存活于程序之外的,这种数据可以不受程序控制,甚至在程序执行结束都可以存在。有两个 基本的例子是流对象和持久化对象。在流对象中,对象转化成字节流,通常发往另一台机器。在持久化对象中, 对象被存储到磁盘上,因此,即使程序终止,它们仍可以保持自己的状态。这种存储方式的技巧在于:把对象转 化为可以存放到其他媒介上的事物,在需要时可以恢复成常规的、基于RAM的对象。但是这种数据占用的内存还 是要释放的,因为GC是不知道你什么时候需要释放,所以需要程序员自己释放,在Java中提供了finalize,在C# 中提供了IDispose,在这里程序员可以决定释放内存的时机。
举个栗子:一个让两个学生做自我介绍的程序,name和age使用的是默认值
public class Student {
public string name;
public int age;
public void Speak() {
Console.WriteLine(this.name + " : " + this.age);
}
}
public class Test {
public static string str = "xxxxx";
public static void Main(string[] args) {
int num = 2;
for (int i = 0; i < num; i++)
{
Student s = new Student();
s.Speak();
}
}
}
/*程序执行的大致顺序是如下
*
* 程序开始------->>
* Main方法入栈
* 栈区保存num
* for循环进栈
* 加载Student类到方法区(需要开辟name,age,Speak等内存)
* new一个对象s1,从方法区复制一份数据到堆内存并进行初始化
* 方法区拿出Speak方法入栈
* Console.WriteLine方法入栈
* s1参数name入栈,s1参数age入栈
* new一个对象s2,从方法区复制一份数据到堆内存并进行初始化
* 方法区拿出Speak方法入栈
* Console.WriteLine方法入栈
* s2参数name入栈,s2参数age入栈
* for循环出栈
* Main出栈
* <---------程序结束
*
* /
图解一下占用的内存是怎么分配以及何时释放的,程序执行的顺序就是栈的压入过程。