ptmalloc分析之基础二

ptmalloc分析之基础二

      内存管理器为什么难写?在设计内存管理算法时,要考虑什么因素?管理内存这是内存管理器的功能需求。正如设计其它软件一样,质量需求一样占有重要的地位。分析内存管理算法之前,我们先看看对内存管理算法的质量需求有哪些:
      1. 最大化兼容性
      要实现内存管理器时,先要定义出分配器的接口函数。接口函数没有必要标新立异,而是要遵循现有标准(如 POSIX 或者 Win32),让使用者可以平滑的过度到新的内存管理器上。
      2. 最大化可移植性
      通常情况下,内存管理器要向 OS 申请内存,然后进行二次分配。所以,在适当的时候要扩展内存或释放多余的内存,这要调用 OS 提供的函数才行。OS 提供的函数则是因平台而异,尽量抽象出平台相关的代码,保证内存管理器的可移植性。 
      3. 浪费最小的空间
      内存管理器要管理内存,必然要使用自己一些数据结构,这些数据结构本身也要占内存空间。在用户眼中,这些内存空间毫无疑问是浪费掉了,如果浪费在内存管理器身的内存太多,显然是不可以接受的。内存碎片也是浪费空间的罪魁祸首,若内存管理器中有大量的内存碎片,它们是一些不连续的小块内存,它们总量可能很大,但无法使用,这也是不可以接受的。
      4. 最快的速度
      内存分配/释放是常用的操作。按着 2/8 原则,常用的操作就是性能热点,热点函数的性能对系统的整体性能尤为重要。
      5. 最大化可调性(以适应于不同的情况)
      内存管理算法设计的难点就在于要适应用不同的情况。事实上,如果缺乏应用的上下文,是无法评估内存管理算法的好坏的。可以说在任何情况下,专用算法都比通用算法在时/空性能上的表现更优。为每种情况都写一套内存管理算法,显然是不太合适的。我们不需要追求最优算法,那样代价太高,能达到次优就行了。设计一套通用内存管理算法,通过一些参数对它进行配置,可以让它在特定情况也有相当出色的表现,这就是可调性。
       6. 最大化局部性(Locality)
      大家都知道,使用 cache 可以提高程度的速度,但很多人未必知道 cache 使程序速度提高的真正原因。拿 CPU 内部的 cache 和 RAM 的访问速度相比,速度可能相差一个数量级。两者的速度上的差异固然重要,但这并不是提高速度的充分条件,只是必要条件。另外一个条件是程序访问内存的局部性(Locality)。大多数情况下,程序总访问一块内存附近的内存,把附近的内存先加入到 cache 中,下次访问 cache 中的数据,速度就会提高。否则,如果程序一会儿访问这里,一会儿访问另外一块相隔十万八千里的内存,这只会使数据在内存与 cache 之间来回搬运,不但于提高速度无益,反而会大大降低程序的速度。因此,内存管理算法要考虑这一因素,减少 cache miss 和 page fault。
      7. 最大化调试功能
       作为一个 C/C++程序员,内存错误可以说是我们的噩梦,上一次的内存错误一定还让你记忆犹新。内存管理器提供的调试功能,强大易用,特别对于嵌入式环境来说,内存错误检测工具缺乏,内存管理器提供的调试功能就更是不可或缺了。
       8. 最大化适应性
       前面说了最大化可调性,以便让内存管理器适用于不同的情况。但是,对于不同情况都要去调设置,无疑太麻烦,是非用户友好的。要尽量让内存管理器适用于很广的情况,只有极少情况下才去调设置。设计是一个多目标优化的过程,有些目标之间存在着竞争。如何平衡这些竞争力是设计的难点之一。在不同的情况下,这些目标的重要性又不一样,所以根本不存在一个最好的内存分配算法。
        一切都需要折衷:性能、易用、易于实现、支持线程的能力等,为了满足项目的要求,有很多内存管理模式可供使用。每种模式都有大量的实现,各有其优缺点。内存管理的设计目标中,有些目标是相互冲突的,比如最快的分配、释放速度与内存的利用率,也就是内存碎片问题。不同的内存管理算法在两者之间取不同的平衡点。为了提高分配、释放的速度,多核计算机上,主要做的工作是避免所有核同时在竞争内存,常用的做法是内存池,简单来说就是批量申请内存,然后切割成各种长度,各种长度都有一个链表,申请、释放都只要在链表上操作,可以认为是 O(1)的。不可能所有的长度都对应一个链表。很多内存池是假设,A 释放掉一块内存后,B 会申请类似大小的内存,但是 A释放的内存跟 B 需要的内存不一定完全相等,可能有一个小的误差,如果严格按大小分配,会导致复用率很低,这样各个链表上都会有很多释放了,但是没有复用的内存,导致利用率很低。这个问题也是可以解决的,可以回收这些空闲的内存,这就是传统的内存管理,不停地对内存块作切割和合并,会导致效率低下。所以通常的做法是只分配有限种类的长度。一般的内存池只提供几十种选择。

你可能感兴趣的:(ptmalloc分析之基础二)