自动垃圾回收学习笔记-一些基本概念
2011-05-21 12:53 横刀天笑 阅读(1865) 评论(4) 编辑 收藏 举报栈和堆
首先要明确的是,这里所说的栈和堆与数据结构里所说的Stack和Heap不是一个概念。当然,这里的栈和堆的表现形式或实现形式跟数据结构里的Stack和Heap可能是一模一样。
栈
栈是跟线程相关的。比如在Windows平台上,默认情况下,你创建一个线程会给你1MB的内存空间当做栈(对于Native Code来说这1MB是按需提交的,而对于Managed Code来说是创建线程的时候就提交)。这1MB一般来说会用来干这么几件事儿:
线程执行方法时传递参数,方法的局部变量,方法的返回地址等都会保存在这个栈上。
对于线程执行的每个方法都会在这个栈上占一帧,这就是所谓的栈帧。随着线程的执行,栈就像一个跳动的音符,忽高忽低。
我们可以这样来想象:有一个小柜子,柜子的高就是栈大小,我们用一个奇怪的计量单位来描述柜子的高:MB。当我们要调用一个方法时,我们就要在栈上面push一个栈帧,就像我们放入一本书到柜子里。这个栈帧保存着被调用方法的局部变量,传入的参数以及返回地址等信息。当该方法退出时,这个栈帧也会被弹出,就像我们将那本书拿书柜一样。
不过有一些奇怪的调用总是往柜子里放东西,很长时间都不拿出,导致柜子被塞满了,这就是栈溢出。比如递归调用或死循环。
在栈上放东西有个特点:方法退出了,所占用的空间就被回收。
堆
由于栈并不是无限大的。而且不支持随机访问。对于一些很大的对象我们要寻找另一个“柜子”来放它们。这个柜子就是堆。
一般来说,我们总是在堆上开辟一块内存,然后栈上的某一个指针指向这块内存。堆这个柜子和栈的柜子有些不同,堆的柜子的大小是用这个柜子的体积来表示的。你可以随便乱塞一些什么玩意儿进去。
不过跟栈相比,堆的内存分配就不是那么显而易见了。比如在C中我们可以用malloc在对上分配一点内存。在开始的时候我们可能还是顺序分配的(这一次分配的地址跟在上一次分配之后)。后来经过一段时间的分配和释放,堆上出现了很多洞,千疮百孔。如果这个时候我们再想分配一个对象,那我们得找一个合适的洞然后才能将我们的对象放下去啊。那我们得有一个东西将这些洞串起来。在malloc背后就有这么一个链表。它是自由内存空间的链表,当我们调用malloc时,就会遍历这个链表,找到一块足够大的洞,然后将其分两半,一般给我,一般回到链表。
堆的特点是:在堆上分配的空间,必须手动的释放。而且堆中很容易产生很多洞(内存碎片)。内存碎片不仅让内存的分配变得低效,而且会浪费内存(可能有很多空闲的内存,但没有一个能容纳得下你)。
托管堆
托管堆也是堆,只不过是.NET里提出的一个概念,在托管堆上开辟的内存会接受GC的管理,在不再使用后会被GC回收掉。
堆栈
世上本来没有堆栈,只有堆和栈,可是很多文章却将其混淆在一起。堆和栈是两个不同的概念,堆是堆,栈是栈。
关于堆和栈
堆和栈是数据结构中两种基本的结构,也是内存管理中两个核心的概念。围绕这两个概念有很多很详细的内容,本文只是想在讨论垃圾回收之前澄清一些基本概念。