关于win98和win2000的内存堆回收问题,核心编程已经说得很清楚了,但是关于栈的回收方法虽然提及但似乎没有说清楚在此有如下疑问:
98会尽可能快的收回不被使用的堆中的内存块,而2000重视速度所以它往往较长时间占用物理存储器,只有在一段时间后页面不再使用时,才将它返回给页文件,所以堆内存的回收原理比较明白了。不过栈的内存又是怎么回收的呢?
是在一个有局部变量的函数返回时,就立即回收栈内的这些局部变量的内存?还是依然遵循2000的堆回收规则呢

解决方案 »

  1.   

    栈变量在离开变量视口(函数return或者大括号结束)的时候自动执行析构函数(如果有的话)。void func()
    {
      CBird bird; // bird 变量入栈,调用构造函数  { // <---- 代码块视口
        CCat cat; // cat 变量入栈,调用构造函数
        cat.EatFish();
      } // <----- 视口结束,这里会放置 cat.~CCat() 调用代码,cat生命期结束,出栈  bird.Fly();
      return;
    } // <----- 函数结束,这里会生成 bird.~CBird() 调用代码,bird生命期结束,出栈
      

  2.   

    栈空间是在线程创建的时候分配的,控制栈的汇编指令通常就是 push 和 pop,涉及的寄存器主要是 esp 和 ebp,局部变量的分配或者函数调用过程中的参数传递通常是通过改变 ebp 的值来完成的,这部分由编译器完成,通常线程默认的栈空间是 1MB,可以通过编译器选项修改,而初始化以后栈的空间大小就不再改变
    你需要理解影响栈的使用的操作通常是:局部变量的定义,函数调用的参数传递和eip寄存器入栈/出栈
    栈的回收是在线程结束时由系统完成的
    也就是说,线程的生命周期决定了栈的生命周期
      

  3.   

    希望各位能仔细看一下我说的意思,我没有要问栈在什么时候销毁(线程退出时),编译器默认栈是多大保留空间(1MB),我也没问栈在分配时保留的1MB空间有多少被提交了页面,我的意思是指当函数退出时,局部变量的内存是否被“回收”,回收!不是指对整个栈的操作是指对栈内内存块的操作,所以这里用回收是很严谨的,内存提交的相反行为就是回收,再详细解释就是把栈内的内存块标记为“保留”!我感觉是系统对保留和已提交标志进行了管理而不是编译器的代码,编译器的代码似乎只是进行对系统调用,来声明哪个内存块已被释放,所以在此请了解的说下
      

  4.   

    好吧。那么我来给你好好讲讲函数退出后,局部变量的内存“可能”被回收,这就是为什么VC的内存泄露是一个被问了无数遍的问题的缘故。如果不人为进行回收,那么基本类型的数据将会被回收,堆栈被平衡之后,他们也就被销毁。但是内核对象将保留在虚拟内存页中,比如GDI对象和句柄对象,他们属于内核对象。随着换页的产生,他们有一定的几率被换出内存(这个取决于命中率),其余的将留在映射的这块虚拟内存中。
    当进程销毁后,这块虚存被释放,残留的对象才被清理干净
      

  5.   

    这个不用猜,有的书上都有讲的,而且NT系统源代码也可以看到,现在的XP Vista都是基于NT的
      

  6.   

    在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 
    当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。我想,应该有一定的手段来跟踪这个过程,可以上网找一下。 
    这篇文章应该对你有用。http://www.cppblog.com/oosky/archive/2006/01/21/2958.html
      

  7.   

    希望各位能仔细看一下我说的意思,我没有要问栈在什么时候销毁(线程退出时),编译器默认栈是多大保留空间(1MB),我也没问栈在分配时保留的1MB空间有多少被提交了页面,我的意思是指当函数退出时,局部变量的内存是否被“回收”,回收!不是指对整个栈的操作是指对栈内内存块的操作,所以这里用回收是很严谨的,内存提交的相反行为就是回收,再详细解释就是把栈内的内存块标记为“保留”! 我感觉是系统对保留和已提交标志进行了管理而不是编译器的代码,编译器的代码似乎只是进行对系统调用,来声明哪个内存块已被释放,所以在此请了解的说下
    -------------------------------------------
    局部变量对应到栈里的数据空间,没有回收的概念void f()
    {
    int x = 0;
    int y = 0;
    int z = 0;
    }这行代码,编译器会为他生成 
    sub ebp, n ;n是指为局部空间保留的空间大小
    mov [ebp+n1],0 ;x=0
    mov [ebp+n2],0 ;y=0
    mov [ebp+n3],0 ;z=0
    是用这种方式为局部变量分配空间的,即编译器知道保留多少空间,哪个地址是哪个局部变量用的
    需要注意的是,在当前线程中,栈只有一个,局部变量的空间是在这个栈中
    f函数退出时
    add ebp, n ;这就相当于把空间还给栈了
    我这里省略了进入f函数push ebp和保存esp原始寄存器值的部分和退出f函数恢复相应寄存器的部分
      

  8.   

    保留和提交是对整个栈的处理,局部变量使用的是栈内的数据,也就是说是使用已分配的内存空间(位于1MB<默认>栈的内存空间中),怎么使用是编译器协助完成的
      

  9.   

    void a()
    {
      int x = 0;
      f();
    }对于函数调用,在进入a的时候,编译器生成的代码为a中的局部变量x分配空间,只是改变ebp寄存器的位置(使其指向栈空间的某个地址),假设x的地址是 0xFFFC
    当执行到函数f时,会有 push ebp, sub ebp,n 这样的代码使 f中的变量使用 0xFFF0 - 0xFFFB 这段地址的内存
    f 执行完以后 0xFFF0-0xFFFB 就不属于 f 函数中的局部变量了,但是这段内存地址的空间并不会回收带参数的函数会稍复杂一些,不过也只是将参数所需要的数据 push 到栈中(会改变esp的值,在被调用函数中仍然以 ebp+偏移 的方式来访问)栈是一个连续的线性地址空间,它的使用是至顶向下的,函数调用层次的加深就是栈使用空间的偏移从上到下递减的过程