【数据结构】动态内存管理

来源:互联网 发布:米卡有软件 编辑:程序博客网 时间:2024/06/14 09:01

动态内存管理是和操作系统息息相关的一个活动,现在的计算机的内存分配和回收基本上都由OS来维护管理,但是一些高级程序语言的内存回收和分配都还是由程序员来管理,比如C和C++,有malloc方法,也总有构造函数和析构函数。但是有一些语言就不需要,比如Python。

系统每次对申请内存的对象分配一段地址连续的内存块,如果已经被占用了,就叫做占用块,如果还没被分配掉,就叫做可利用空间块或者空闲块。在回收内存之后,系统的整个内存块会出现参差不齐的状态,这时候就需要用到动态内存管理了。

一、 可利用空间表

可利用空间表是系统中都在使用的一种内存管理工具,针对不同的情况需要设定不同的链表数据结构。
1、 每次分配的内存大小是一样的,这时候就使用一个链栈即可,把内存分为大小相同的块,如果有应用程序来申请,就从栈顶分配一个内存块出去,如果回收回来一个,就放入栈顶。
2、 系统每次分配给用户的内存块的大小分为若干种规格,且一般都是2的次方。这种情况下采用可利用空间表,且利用伙伴系统对内存进行管理。
3、 系统每次按需分配给用户内存块,大小随机。这种情况下同样适用可利用空间表,只不过数据结构会有所变化,管理手段也有不同。

二、 可利用空间表的几种数据结构

1、普通可利用空间表:即一个可利用空间链表,其中包括以下几个字段,tag标识该段是否被使用,type或size标识该段的内存空间大小,link标识后继的内存块,space即存储的空间。
2、采用边界标识法的可利用空间表:相比普通可利用空间表要复杂一些,主要的字段有llink即该内存块的前驱,rlink是后继,tag 和 size 不变,uplink标识也是指针,指向本链表结点,其值就是该空闲内存块的首地址。分配的时候需要修改链表,也就是修改若干指针的地址;回收的时候要考虑相邻的内存块是否是空闲块,如果是的话,就要合并。
3、采用伙伴系统的可利用空间表:通过一个2的次方的指针数组来分别指示不同大小块的链表,分配的时候也是修改链表,但是在回收的时候,同样存在相邻空闲块合并的操作,但是如果两个相邻块不是同一个大的内存块分化出来的,则不合并,因为容易造成错误。

三、分配内存策略

1、就近分配,在搜索可利用空间链表的过程中,遇到的第一个符合应用程序申请要求的内存块就被系统选中分配出去。
2、最优化匹配,在搜索可利用空间表的过程中,要选择空间最接近申请要求的内存块,这样能够避免块内存的浪费。这种分配方法较为复杂,也就是说在分配的时候需要修改指针,回收的时候还是需要修改指针,但是好处是比较适合那些申请内存大小千差万别的系统。
3、最差匹配,在搜索可利用空间表的过程中,要选择最不接近申请要求的内存块,也就是最大的那一个空闲块。这个策略比较适合每次申请的内存块差不多大小的系统。

四、 无用单元收集

1、 设置内存计数器,如果指向某内存单元的指针计数为0,则释放该单元;
2、 收集无用单元:主要包括两个步骤,给所有的占用块添加mark域,当mark为0时表示该结点还没有被判断过是否要收集无用单元,mark为1表示已经被系统考察过了;然后,遍历所有的占用块,这种标记遍历算法有若干种,具体不述了。

五、 存储紧缩

无用单元收集的过程主要就是修改单元中的指针和某些结点的标记值,而存储紧缩则要将数据按照内存地址进行整体迁移,其过程相当于搬家。这样能完整地改善内存块犬牙交错的状态。

六、Python的内存管理

  1. 对象的引用计数机制
    Python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数。
    引用计数增加的情况:
    1,一个对象分配一个新名称
    2,将其放入一个容器中(如列表、元组或字典)
    引用计数减少的情况:
    1,使用del关键字语句对对象别名显示的销毁
    2,引用超出作用域或被重新赋值
    sys.getrefcount( )函数可以获得对象的当前引用计数
    多数情况下,引用计数比你猜测得要大得多。对于不可变数据(如数字和字符串),解释器会在程序的不同部分共享内存,以便节约内存。
  2. 垃圾回收
    1,当一个对象的引用计数归零时,它将被垃圾收集机制处理掉,也就是无用单元收集的一种策略。
    2,当两个对象a和b相互引用时,del语句可以减少a和b的引用计数,并销毁用于引用底层对象的名称。然而由于每个对象都包含一个对其他对象的应用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。
  3. 内存池机制
    python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。
    1,Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。
    2,Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的malloc。
    3,对于Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象间不共享他们的内存池。也就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。
  4. 分代技术
    分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。
    Python默认定义了三代对象集合,索引数越大,对象存活时间越长。
    举例:
    当某些内存块M经过了3次垃圾收集的清洗之后还存活时,我们就将内存块M划到一个集合A中去,而新分配的内存都划分到集合B中去。当垃圾收集开始工作时,大多数情况都只对集合B进行垃圾回收,而对集合A进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合B中的某些内存块由于存活时间长而会被转移到集合A中,当然,集合A中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。
原创粉丝点击