第五问来啦!
5
free
的内存真的释放了吗(还给 OS ) ?
前面所有例子都有一个很严重的问题,就是分配的内存都没有释放,即导致内存泄露。
原则上所有 malloc/new 分配的内存,都需 free/delete 来释放。但是, free 了的内存真的释放了吗?
要说清楚这个问题,可通过下面例子来说明。
(1) 初始状态
如图 (1) 所示,系统已分配 ABCD 四块内存,其中 ABD 在堆内分配, C使用 mmap 分配。为简单起见,图中忽略了如共享库等文件映射区域的地址空间。
(2) E=malloc(100k)
分配 100k 内存,小于 128k ,从堆内分配,堆内剩余空间不足,扩展堆顶 (brk) 指针。
(3) free(A)
释放 A 的内存,在 glibc 中,仅仅是标记为可用,形成一个内存空洞 ( 碎片 ) ,并没有真正释放。如果此时需要分配 40k 以内的空间,可重用此空间,剩余空间形成新的小碎片。
(4) free(C)
C 空间大于 128K ,使用 mmap 分配,如果释放 C ,会调用 munmap系统调用来释放,并会真正释放该空间,还给 OS ,如图 (4) 所示。
(5) free(D)
与释放 A 类似,释放 D 同样会导致一个空洞,获得空闲空间,但并不会还给 OS 。此时,空闲总空间为 100K ,但由于虚拟地址不连续,无法合并,空闲空间无法满足大于 60k 的分配请求。
(6) free(E)
释放 E ,由于与 D 连续,两者将进行合并,得到 160k 连续空闲空间。同时 E 是最靠近堆顶的空间, glibc 的 free 实现中,只要堆顶附近释放总空间(包括合并的空间)超过 128k ,即会调用 sbrk(-SIZE) 来回溯堆顶指针,将原堆顶空间还给OS ,如图 (6) 所示。而堆内的空闲空间还是不会归还 OS 的。
由此可见
1. malloc 使用 mmap 分配的内存 ( 大于 128k) , free 会调用 munmap 系统调用马上还给 OS ,实现真正释放。
2. 堆内的内存,只有释放堆顶的空间,同时堆顶总连续空闲空间大于 128k 才使用sbrk(-SIZE) 回收内存,真正归还 OS 。
3. 堆内的空闲空间,是不会归还给 OS 的。
【往期精彩回顾】