7

我的理解是,在 unix 中,当内存被释放时,内存不会返回给操作系统,它会留在进程中以再次用于下一次调用 malloc。

在 Windows 上,我知道内存实际上是返回给操作系统的。

这两种做事方式之间有什么大区别,还是只是做同一件事的两种不同方式?如果这两种方法有任何优点/缺点,它们是什么?

编辑: 感谢您的澄清。我一直认为这是操作系统的问题(因为在类 UNIX 系统中进程的大小似乎从未减小,但在 Windows 中却如此)。

4

9 回答 9

15

在这方面,Windows 和 Unix 之间没有太大区别。

两者都有两个级别的分配。操作系统以大块(一页或更多;在 x86 上,页面大小通常为 4096 字节)为进程分配内存。在进程中运行的运行时库会细分该空间并将其中的一部分分配给您的代码。

要将内存返回给操作系统,首先必须将从这些大块之一分配的所有内存释放到运行时库。如果需要,运行时库可以告诉操作系统释放那块内存。

在 Linux 上,您有brkmmap. brk控制分配给您的进程的大块内存的大小;你可以扩大或缩小它,但只能在一端。malloc传统上,当它需要更多内存来分配时,会扩展这块内存,并在可能的情况下缩小它。但是,缩小并不容易;最后需要一个单字节的不合时宜的分配,使其无法收缩,即使在该分配之前的所有内容都已被释放。这就是“Unix 不释放内存”模因的来源。

但是,也有匿名的mmap。Anonymousmmap从操作系统请求一块内存,它可以放在进程内存空间的任何地方。当不再需要这个块时,可以很容易地返回它,即使有后来的分配还没有释放。malloc也使用mmap(特别是对于大型分配,其中一整块内存在被释放后可以很容易地返回)。

当然,在 Windows 和 Linux 上,如果您不喜欢运行时库中的内存分配器(或分配器)的行为,您可以使用自己的,从操作系统询问内存并按照您想要的方式(或者有时从另一个分配器请求内存,但在更大的块中)。一个有趣的用途是为与任务相关的所有内存分配器(例如,Web 服务器请求),在任务结束时完全丢弃(无需单独释放所有部分);另一个有趣的用途是固定大小对象(例如,五字节对象)的分配器,它可以避免内存碎片。

于 2008-10-25T12:40:45.713 回答
6

请注意,在接下来的内容中,我对 Windows 的了解比对 Unix 的多得多……

无论如何,内存分配和释放实际发生的情况并不完全是您所描述的。这是因为这里有两个非常不同的概念在起作用:计算机拥有的物理内存,以及程序的虚拟地址空间,即您的程序认为它可以使用的内存。

当您的程序向操作系统请求更多内存时,实际发生的是程序中以前不可用的虚拟地址空间被设置为程序可访问。现代操作系统不能仅仅通过拥有一个“真实”(即物理)内存池来工作,当它们发出分配请求时它会分发给进程:它为每个正在运行的程序维护虚拟地址空间,并且,当程序实际访问该虚拟地址空间的一部分,确保将其映射到某些物理内存,可能通过将另一个程序的地址空间的某些部分换出到磁盘上的交换文件。

例如,在 Windows 上,每个线程都以(默认情况下)为其分配的 1 MB 堆栈空间开始。这并不意味着每个线程都消耗一兆字节的机器物理内存:只是设置了地址空间以便它可以使用。从这个意义上说,考虑操作系统给你的程序内存然后程序把它还给它是行不通的——它只是不能那样工作。

于 2008-10-25T07:22:12.453 回答
4

这完全取决于您使用的 C 运行时库。没有特定的 UNIX 方式或 WINDOWS 方式。每个编译器供应商(HP、SUN、MS、GNU)都附带了他们自己的包含 malloc 逻辑的运行时库。malloc 的每个实现将根据操作系统运行相同/不同。UNIX/LINUX/Windows 都不需要免费的“实际返回”内存给操作系统。那太贵了(因为你的 alloc() 会是非常小的块)

最近 mozilla Firefox 从 *BSD OS 借用了 malloc() 实现。他们选择使用与他们的编译器供应商(在本例中为多个——gcc 和 VC++)提供的不同的 malloc。既然他们想要某种行为,他们就得到了他们想要的。

于 2008-10-25T07:30:59.927 回答
3

正如其他提到的,这与 malloc 实现的联系比操作系统本身更多。在 linux 上,使用 glibc,内存实际上总是返回给操作系统,超过一定大小:glibc malloc 使用 mmap 进行大分配(由 MMAP_THRESHOLD 控制),在这种情况下,free 调用 munmap,它会自动释放保留的内存。低于该阈值,它使用 brk,并且 free 在这种情况下不会“返回”内存。

请注意,上面的解释并不准确:准确地说,你需要知道物理内存、虚拟内存等之间的区别……这里解释得很好:

http://blogs.msdn.com/oldnewthing/archive/2004/08/22/218527.aspx

于 2008-10-25T08:37:33.193 回答
2

从这篇内存管理文章

Malloc 通常不会将释放的内存返回给操作系统;它一直归进程所有,直到它终止。该进程可以在下次请求更多内存时重用它,但其他程序将无法访问它,即使没有其他内存可用。作为推论,程序的内存占用量是在任何时候进行的最大分配的大小。因此,明智的做法是尽快释放不需要的对象,尤其是大型对象,以尽量减少占用空间。

那篇文章确实建议在 Windows 中,至少对于 C 程序,内存不会返回给操作系统。

因此,我不确定您对 Windows 内存释放的概括。

也就是说,您可以通过在 Windows 上实现低级系统调用 sbrk 和 mmap/munmap来尝试在 Microsoft Windows 下模拟 UNIX 内存管理。

于 2008-10-25T06:32:28.750 回答
2

唯一不能轻易将分配的内存归还给系统的操作系统是 OS X - 引用Firefox 3 Memory Usage

经过 Apple 员工的广泛测试和确认后,我们意识到分配器无法在保留地址范围的同时归还未使用的内存页面。(您可以取消映射它们并重新映射它们,但这会导致一些竞争条件并且是'不如性能。)有些 API 声称可以做到这一点(madvise() 和 msync()),但它们实际上并没有做任何事情。

于 2008-10-25T08:01:52.127 回答
1

我不了解 Windows,但在 UNIX 上,brk()调用用于将更多内存带入地址空间以供malloc()调用使用。

在进程终止之前,我从未见过此内存返回操作系统。您通常可以使用诸如top.

我怀疑 Windows 的行为是相同的,但我知道 Windows 有其他分配功能,而不是malloc()可以这样做(Win32 API 的一部分)。

于 2008-10-25T06:37:05.493 回答
1

其他发帖者则对平台特定角度发表了评论。但是由于您专门询问 malloc,让我们看看 C 标准是怎么说的:

“free 函数导致 ptr 指向的空间被释放,也就是说,可用于进一步分配。”

这似乎是一个非常明确的要求,即内存不会返回给操作系统。你偶尔会看到依赖这种行为的程序:

int main(void)
{

    void *p = malloc(AS_MUCH_MEMORY_AS_I_WILL_EVER_NEED);

    if (p != 0)
    {
        free(p);
        /* malloc should always work for rest of program */
    }
}

然而,当这个问题出现在 comp.lang.c 上时,一些发帖者指出了这一部分:

“malloc 函数返回空指针或指向分配空间的指针。”

这表明对 malloc 的任何调用都可能失败。该标准的意图似乎是不将内存归还给操作系统,但在语言律师看来,这个问题并不是 100% 确定的。

于 2008-10-25T08:38:03.643 回答
0

malloc 函数返回空指针或指向已分配空间的指针。”

这表明对 malloc 的任何调用都可能失败。似乎该标准的意图是不将内存返回给操作系统。

于 2015-03-18T07:21:44.037 回答