内存碎片是怎么产生的?有解决办法?

时间:2021-11-06 22:46:27
用malloc申请空间时,不是都有用free释放吗?为什么还会产生内存碎片,有什么办法可以解决这个问题?

7 个解决方案

#1


是每次都有释放,不过释放的时间不同就会产生内存碎片,比如说,在内存单元100的起始地址到内存单元200之间,一共申请了100块1字节的区域,但是释放的时候,先释放了内存地址为基数的单元,如释放101、103...而偶数单元不释放,释放50次后,虽然还有50字节的内存是空余的,但是如果下次要申请2字节的内存单元,是无法在100到200之间申请到的,因为这个区域没有连续的2字节空间,这就是内存碎片。
解决的办法就是自己写一个算法来管理内存分配,比如一次申请一大块区域,再将这块区域分成不同的小块,每次要用的时候将小块分配出去,释放的时候大块一起释放,大概就是这个意思了,很困了,睡觉先。。。。

#2


看一下 内存池 -_-

#3


这个不是你应用程序代码考虑的事吧?如果要考虑也是malloc与free实现或者

OS堆释放优化的问题。

#4


内存碎片通常分为内部碎片和外部碎片:

  1. 内部碎片是由于采用固定大小的内存分区,当一个进程不能完全使用分给它的固定内存区域时就产生了内部碎片,通常内部碎片难以完全避免;

  2. 外部碎片是由于某些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求,从而不能被进程利用的内存区域。

  现在普遍采用的段页式内存分配方式就是将进程的内存区域分为不同的段,然后将每一段由多个固定大小的页组成。通过页表机制,使段内的页可以不必连续处于同一内存区域,从而减少了外部碎片,然而同一页内仍然可能存在少量的内部碎片,只是一页的内存空间本就较小,从而使可能存在的内部碎片也较少。

#5


引用 1 楼 jiemo1123 的回复:
是每次都有释放,不过释放的时间不同就会产生内存碎片,比如说,在内存单元100的起始地址到内存单元200之间,一共申请了100块1字节的区域,但是释放的时候,先释放了内存地址为基数的单元,如释放101、103...而偶数单元不释放,释放50次后,虽然还有50字节的内存是空余的,但是如果下次要申请2字节的内存单元,是无法在100到200之间申请到的,因为这个区域没有连续的2字……
想法不错,不过好像不太好用啊

#6


引用 5 楼 lovehuan54 的回复:
引用 1 楼 jiemo1123 的回复:是每次都有释放,不过释放的时间不同就会产生内存碎片,比如说,在内存单元100的起始地址到内存单元200之间,一共申请了100块1字节的区域,但是释放的时候,先释放了内存地址为基数的单元,如释放101、103...而偶数单元不释放,释放50次后,虽然还有50字节的内存是空余的,但是如果下次要申请2字节的内存单元,是无法在100到20……

有用的,在嵌入式开发过程中,能使用的内存比较小,这种方法就会有用处

#7


内存碎片的确是个问题,所以有时候我们要平衡

能够知道数组大小情况下,就尽量用数组。(当然前提是不要栈越界)

数组大小未知,但是最大范围比较小(比如不超过100字节,这个100只是举例)情况下,我们定义 char a[100]就比较恰当。

数组大小未知,最大范围也未知;或者可能超过几K,那么动态申请就是一个比较好的选择。
曾经在我们项目组一个日志函数,因为使用了变参,出现了好几次把栈踩坏的问题。这时就应该使用动态申请了。

#1


是每次都有释放,不过释放的时间不同就会产生内存碎片,比如说,在内存单元100的起始地址到内存单元200之间,一共申请了100块1字节的区域,但是释放的时候,先释放了内存地址为基数的单元,如释放101、103...而偶数单元不释放,释放50次后,虽然还有50字节的内存是空余的,但是如果下次要申请2字节的内存单元,是无法在100到200之间申请到的,因为这个区域没有连续的2字节空间,这就是内存碎片。
解决的办法就是自己写一个算法来管理内存分配,比如一次申请一大块区域,再将这块区域分成不同的小块,每次要用的时候将小块分配出去,释放的时候大块一起释放,大概就是这个意思了,很困了,睡觉先。。。。

#2


看一下 内存池 -_-

#3


这个不是你应用程序代码考虑的事吧?如果要考虑也是malloc与free实现或者

OS堆释放优化的问题。

#4


内存碎片通常分为内部碎片和外部碎片:

  1. 内部碎片是由于采用固定大小的内存分区,当一个进程不能完全使用分给它的固定内存区域时就产生了内部碎片,通常内部碎片难以完全避免;

  2. 外部碎片是由于某些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求,从而不能被进程利用的内存区域。

  现在普遍采用的段页式内存分配方式就是将进程的内存区域分为不同的段,然后将每一段由多个固定大小的页组成。通过页表机制,使段内的页可以不必连续处于同一内存区域,从而减少了外部碎片,然而同一页内仍然可能存在少量的内部碎片,只是一页的内存空间本就较小,从而使可能存在的内部碎片也较少。

#5


引用 1 楼 jiemo1123 的回复:
是每次都有释放,不过释放的时间不同就会产生内存碎片,比如说,在内存单元100的起始地址到内存单元200之间,一共申请了100块1字节的区域,但是释放的时候,先释放了内存地址为基数的单元,如释放101、103...而偶数单元不释放,释放50次后,虽然还有50字节的内存是空余的,但是如果下次要申请2字节的内存单元,是无法在100到200之间申请到的,因为这个区域没有连续的2字……
想法不错,不过好像不太好用啊

#6


引用 5 楼 lovehuan54 的回复:
引用 1 楼 jiemo1123 的回复:是每次都有释放,不过释放的时间不同就会产生内存碎片,比如说,在内存单元100的起始地址到内存单元200之间,一共申请了100块1字节的区域,但是释放的时候,先释放了内存地址为基数的单元,如释放101、103...而偶数单元不释放,释放50次后,虽然还有50字节的内存是空余的,但是如果下次要申请2字节的内存单元,是无法在100到20……

有用的,在嵌入式开发过程中,能使用的内存比较小,这种方法就会有用处

#7


内存碎片的确是个问题,所以有时候我们要平衡

能够知道数组大小情况下,就尽量用数组。(当然前提是不要栈越界)

数组大小未知,但是最大范围比较小(比如不超过100字节,这个100只是举例)情况下,我们定义 char a[100]就比较恰当。

数组大小未知,最大范围也未知;或者可能超过几K,那么动态申请就是一个比较好的选择。
曾经在我们项目组一个日志函数,因为使用了变参,出现了好几次把栈踩坏的问题。这时就应该使用动态申请了。