Ruby 黑客指南/垃圾回收
在深入本章内容之前,让我们回顾一下程序执行时内存的组织方式。本章将涉及计算机体系结构的一些底层组件,因此事先熟悉一些基本概念是必要的。此外,这些概念也将在后面的章节中用到。让我们在这里把它们解决掉。
大多数 C 程序在它们的内存空间中都有以下组件
- 文本区域,
- 静态和全局变量的存储,
- 机器栈,
- 堆。
文本区域是存储代码的地方。第二个组件应该很清楚。机器栈是存放函数的参数和局部变量的地方。堆是通过malloc()
分配的。
让我们详细讨论一下机器栈。被称为机器“栈”,它显然具有类似栈的结构。换句话说,我们可以不断地向顶部添加新元素。在实践中,值以单独的int
单元添加到堆栈中,但在概念上有一个称为堆栈帧的较大单元。
每个堆栈帧对应一次函数调用。换句话说,每个函数调用都会添加一个堆栈帧,并且在return
时会移除一个堆栈帧。如果我们极度简化这一点,机器栈可能看起来像图 1。
the top +-------------+ | stack frame | <-- the frame for the currently running function +-------------+ | stack frame | +-------------+ | stack frame | +-------------+ | stack frame | +-------------+ | stack frame | +-------------+ the bottom
图 1:机器栈
在这张图中,我们标记了栈的极端端为“顶部”,但机器栈并不一定从低到高地寻址帧。例如,在 x86 机器上,栈从较高地址向较低地址增长。
使用malloc()
可以分配任意大小的内存。alloca()
是机器栈的版本。但是,通过alloca()
分配的内存不需要释放。或者,更确切地说,可以更好地说是内存随着函数的return
而“被”释放。因此,alloca()
分配的值不能用作函数的return
值。这与“不能返回指向局部变量的指针”相同。
所有这一切都很好。它基本上意味着我们可以本地分配长度动态变化的数组。
但是,有一些环境没有本机alloca()
。许多人会希望在这些环境中也使用alloca()
,因此可以用 C 编写具有相同行为的函数。但是,在这种情况下,它可能只被实现为“不需要释放”,但可能不一定会分配机器栈上的内存。事实上,它通常不会这样做。如果它能够做到这一点,那么可能也存在alloca()
的本机实现。
我们如何在 C 中实现alloca()
?最直接的实现首先使用malloc()
分配内存。然后,它将调用者函数和分配的地址存储在全局列表中。然后,下次调用alloca()
时,如果存在为已经结束的函数分配的任何内存,就可以将其free()
掉(见图 2)。
+-----------+ +------------+ | main | | main | +-----------+ +------------+ | A | ===> | A | +-----------+ +------------+ | B | | B | mark that B -> alloca(32) +-----------+ | alloca(32) | free the memory allocated for D | C | +------------+ +-----------+ | D | | alloca(8) | mark that D -> alloca(8) +-----------+
图 2:C 实现的alloca()
的行为
Ruby 的missing/alloca.c
就是这样一个模拟alloca()
的实现。
现在让我们开始本章的主题,垃圾回收。
<hline>
评论、建议和批评可以发送到 Aoki MINERŌ <[email protected]>。 请将翻译评论、建议和批评发送到本章译者 mitcho (Michael Yoshitaka Erlewine) <[email protected]>。