动态内存分配是如何工作的

3
在C/C++中,当您想要动态分配内存时,通常会调用malloc void* malloc(size_t size); 以返回一个指向大小为size字节的内存块的指针。使用完这个内存块后,您需要调用free()将内存释放回堆。
这很好,但是如果您调用realloc void* realloc(void* ptr, size_t size); 来更改ptr指向的内存块的大小,那么您仍然需要在使用内存后调用free(),但我的问题是编译器如何知道要释放多少内存?
直觉上,我可以想出一个答案,但我对实现细节感兴趣-它是如何真正实现的?它是否与编译器相关?它是标准的一部分吗?
谢谢!

1
在 C 语言中,你会使用 malloc 函数,但在 C++ 中则不需要。 - David Rodríguez - dribeas
我看到它在C/C++中都被使用了; 不管是不是正确的,这是另一回事。 - Pandrei
2
简短回答:这要看情况。有许多malloc/free算法,其中一些针对不同的使用模式进行了优化。此外,编译器并不知道这些例程,它们是由库实现的。 - SirDarius
1
另一个问题中没有出现的答案是,内存管理是动态的,而不是在编译时确定的。mallocfree(以及realloccalloc等)是库函数,它们有自己的数据结构,并动态跟踪内存分配。实际上并没有标准(尽管有一些常用算法),因为库可以选择按照自己的方式实现内存管理。 - Michael
1个回答

3
为了释放内存,通常不需要说明要释放多少内存。分配器已经知道它给了你多少内存,并记住了这个。分配的内存地址作为分配器内部簿记数据结构的一种“键”。因此,你只需要提供要释放的内存的地址,分配器就知道你在谈论哪个内存。
需要明确的是:没有“部分释放”某些内存的情况。你不能指向一些已分配的内存的中间部分并说“在这里释放100字节”。你只能释放你获得的精确数量的内存,不多也不少。
最后,realloc只是mallocmemcpy的组合,如果你愿意,也许有一个小的优化机会,允许现有的分配“增长”而不移动数据。但是思路是相同的。

操作系统进行账目管理。书籍《深入理解Linux内核》详细介绍了Linux如何进行这项工作(尽管该书现在已经有些过时)。 - Will
@Will:操作系统通常进行更粗略的簿记。分配器库将希望从操作系统中获取大块内存,并自行处理细粒度用户分配。 - Kerrek SB
在这个上下文中,值得一提的是什么是大的和小的?也就是说,如果你释放一个字节,那么它将由分配器处理。如果你释放100k,则操作系统将处理它。 - Will
@Will:我认为现代分配器从不向操作系统释放内存。 - Kerrek SB
1
@Will:glibc malloc在进行大型分配(超过可调整的阈值,如64k)时使用mmap,因此它肯定可以在free时将它们交还给内核。如果程序需要大量内存,但随后释放了大部分内存并长时间运行,则避免保留可能大量脏匿名页面的使用。让它们被交换到磁盘上会使最终重用它们变得更慢(硬页错误),而不是稍后再次请求内核页面(通常是由于惰性分配引起的软页错误)。 - Peter Cordes
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接