mah*_*esh 263 c c++ malloc free memory-management
我想知道如何malloc
和free
工作.
int main() {
unsigned char *p = (unsigned char*)malloc(4*sizeof(unsigned char));
memset(p,0,4);
strcpy((char*)p,"abcdabcd"); // **deliberately storing 8bytes**
cout << p;
free(p); // Obvious Crash, but I need how it works and why crash.
cout << p;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果答案在记忆水平上是深入的,如果可能的话,我将非常感激.
Jue*_*gen 372
好的,关于malloc的一些答案已经发布了.
更有趣的部分是免费的工作方式(在这方面,malloc也可以更好地理解).
在许多malloc/free实现中,free通常不会将内存返回给操作系统(或者至少在极少数情况下).原因是你的堆中会出现间隙,因此它可能会发生,你只需要完成2或4 GB的虚拟内存.这应该避免,因为一旦虚拟内存完成,你将遇到很大麻烦.另一个原因是操作系统只能处理特定大小和对齐的内存块.具体来说:通常操作系统只能处理虚拟内存管理器可以处理的块(最常见的是512字节的倍数,例如4KB).
因此,返回40字节到操作系统将无法正常工作.那么免费做什么呢?
Free将把内存块放在自己的空闲块列表中.通常它也会尝试将地址空间中的相邻块融合在一起.空闲块列表只是一个内存块的循环列表,它在开头有一些管理数据.这也是使用标准malloc/free管理非常小的内存元素效率不高的原因.每个内存块都需要额外的数据,而更小的大小会发生更多碎片.
当需要新的内存块时,freeoc列表也是malloc查看的第一个地方.它在从OS调用新内存之前进行扫描.当发现大于所需内存的块时,它被分成两部分.一个返回给调用者,另一个返回到空闲列表中.
对此标准行为有许多不同的优化(例如,对于小块内存).但由于malloc和free必须如此通用,因此当替代品不可用时,标准行为始终是后备.在处理空闲列表时也有优化 - 例如将块存储在按大小排序的列表中.但所有优化也有其自身的局限性.
为什么你的代码崩溃了:
原因是通过将9个字符(不要忘记尾随空字节)写入一个大小为4个字符的区域,您可能会覆盖为存储在您的数据块"后面"的另一块内存存储的管理数据(因为这些数据通常存储在存储器块的"前面".如果空闲然后尝试将您的块放入空闲列表,它可以触摸此管理数据,因此偶然发现覆盖指针.这会使系统崩溃.
这是一种相当优雅的行为.我还看到了某个地方失控指针在无内存列表中覆盖了数据的情况,系统没有立即崩溃,但后来又出现了一些子程序.即使在中等复杂的系统中,这些问题也可能真的非常难以调试!在我参与的一个案例中,我们(一大群开发人员)花了好几天才找到崩溃的原因 - 因为它与内存转储指示的位置完全不同.这就像一颗定时炸弹.你知道,你的下一个"免费"或"malloc"会崩溃,但你不知道为什么!
这些是一些最糟糕的C/C++问题,也是指针可能如此成问题的一个原因.
joe*_*joe 54
正如aluser在这个论坛帖子中所说:
您的进程有一个内存区域,从地址x到地址y,称为堆.您的所有malloc数据都存在于此区域.malloc()保留了堆中所有空闲空间块的一些数据结构,比如说列表.当你调用malloc时,它会在列表中查找一个足够大的块,返回一个指向它的指针,并记录它不再是空闲的事实以及它有多大.当您使用相同的指针调用free()时,free()会查找该块的大小并将其添加回空闲块()列表中.如果你调用malloc()并且它在堆中找不到任何足够大的块,它会使用brk()系统调用来增加堆,即增加地址y并导致旧y和新y之间的所有地址是有效的记忆.brk()必须是系统调用; 没有办法完全从用户空间做同样的事情.
malloc()依赖于系统/编译器,因此很难给出具体的答案.然而,基本上它确实跟踪它分配的内存,并且取决于它的工作方式,因此您对free的调用可能会失败或成功.
malloc() and free() don't work the same way on every O/S.
sam*_*moz 34
malloc/free的一个实现执行以下操作:
Dig*_*oss 25
您的示例代码基本上询问为什么示例程序没有陷阱,答案是内存保护是一个内核功能,仅适用于整个页面,而内存分配器是一个库功能,它管理..没有强制执行..任意大小的块通常比页面小得多.
内存只能以页面为单位从程序中删除,甚至不太可能被观察到.
如果需要,calloc(3)和malloc(3)确实与内核交互以获取内存.但是大多数free(3)的实现都没有将内存返回给内核1,它们只是将它添加到一个空闲列表中,calloc()和malloc()稍后会参考,以便重用已发布的块.
即使free()想要将内存返回给系统,它也需要至少一个连续的内存页才能让内核真正保护该区域,因此释放一个小块只会导致保护更改,如果它是页面中的最后一个小块.
所以你的街区就在那里,坐在免费清单上.您几乎总能访问它和附近的内存,就像它仍然被分配一样.C直接编译为机器代码,没有特殊的调试安排,在加载和存储上没有健全性检查.现在,如果您尝试访问一个空闲块,标准就不会定义该行为,以免对库实现者提出不合理的要求.如果您尝试访问已分配块之外的已释放内存或内存,则可能出现各种错误:
因此,从您的示例向后工作到整体理论,malloc(3)在需要时从内核获取内存,通常以页为单位.这些页面按程序要求进行划分或合并.Malloc和免费合作维护目录.它们在可能的情况下合并相邻的自由块,以便能够提供大块.该目录可能涉及或可能不涉及使用释放块中的存储器来形成链表.(替代方案是更多共享内存和分页友好,它涉及专门为目录分配内存.)即使特殊和可选的调试代码被编译成,Malloc和free几乎没有能力强制访问单个块.该程序.
1.很少有free()实现尝试将内存返回系统的事实不一定是由于实现者的松弛.与内核交互比简单地执行库代码慢得多,而且好处很小.大多数程序具有稳态或增加的内存占用,因此分析堆寻找可返回内存所花费的时间将完全被浪费.其他原因包括内部碎片使页面对齐的块不太可能存在,并且返回块可能会将块分块到任何一侧.最后,几个返回大量内存的程序可能会绕过malloc()并简单地分配和释放页面.
Chr*_*uin 23
理论上,malloc从操作系统获取此应用程序的内存.但是,由于您可能只需要4个字节,并且操作系统需要在页面中工作(通常为4k),因此malloc会做更多的事情.它需要一个页面,并在其中放置自己的信息,以便它可以跟踪您已分配和从该页面中释放的内容.
例如,当你分配4个字节时,malloc会给你一个指向4个字节的指针.您可能没有意识到的是,malloc正在使用4 字节之前的8-12个字节来创建已分配的所有内存的链.当你自由呼叫时,它会将你的指针,备份到它的数据所在的位置,并对其进行操作.
释放内存时,malloc将该内存块从链中取出......并且可能会也可能不会将该内存返回给操作系统.如果是这样,那么访问该内存可能会失败,因为操作系统会剥夺您访问该位置的权限.如果malloc保留内存(因为它在该页面中分配了其他内容,或者进行了一些优化),那么访问将会起作用.这仍然是错的,但它可能会奏效.
免责声明:我所描述的是malloc的常见实现,但绝不是唯一可能的实现.
Ste*_*sop 12
由于NUL终结符,strcpy行尝试存储9个字节而不是8个字节.它调用未定义的行为.
免费电话可能会或可能不会崩溃.分配的4个字节后面的内存可能会被C或C++实现用于其他内容.如果它被用于其他东西,那么在它上面涂鸦会导致"别的东西"出错,但如果它没有被用于其他任何东西,那么你可能碰巧侥幸逃脱它."逃避它"可能听起来不错,但实际上很糟糕,因为这意味着你的代码看起来运行正常,但是在未来的运行中你可能无法逃脱它.
使用调试样式的内存分配器,您可能会发现在那里写入了一个特殊的保护值,并且可以自由检查该值并在没有找到它时发生恐慌.
否则,您可能会发现接下来的5个字节包含属于尚未分配的某个其他内存块的链接节点的一部分.释放块可能会将其添加到可用块列表中,并且因为您已在列表节点中进行了潦草,该操作可能会取消引用具有无效值的指针,从而导致崩溃.
这一切都取决于内存分配器 - 不同的实现使用不同的机制.
Mar*_*age 12
malloc()和free()的工作原理取决于使用的运行时库.通常,malloc()从操作系统分配一个堆(一块内存).每个对malloc()的请求然后分配一小部分内存,返回一个指向调用者的指针.内存分配例程必须存储一些有关分配的内存块的额外信息,以便能够跟踪堆上的已用内存和空闲内存.此信息通常存储在malloc()返回指针之前的几个字节中,它可以是内存块的链接列表.
通过写过malloc()分配的内存块,您很可能会破坏下一个块的一些簿记信息,这些信息可能是剩余未使用的内存块.
编程的一个地方也可能崩溃是将太多字符复制到缓冲区中.如果额外字符位于堆外部,则在尝试写入不存在的内存时可能会出现访问冲突.
小智 6
这与malloc和free没有任何关系.复制字符串后,您的程序会显示未定义的行为 - 它可能会在该点或之后的任何时刻崩溃.即使你从未使用过malloc和free,并且在堆栈上或静态地分配了char数组,这也是正确的.
malloc和free是依赖于实现的.典型的实现涉及将可用内存划分为"空闲列表" - 可用内存块的链接列表.许多实现人为地将其分为小对象和大对象.空闲块首先包含有关内存块有多大以及下一个内存位置等信息.
当你使用malloc时,会从空闲列表中拉出一个块.当你空闲时,该块会被放回到空闲列表中.有可能,当你覆盖指针的末尾时,你正在写在空闲列表中的一个块的标题上.释放内存时,free()会尝试查看下一个块,最终可能会遇到导致总线错误的指针.