由Foxit PDF Creator生成?福昕软件仅用于评估。内存碎片处理技术内存碎片是一个非常困难的问题。如何分配内存确定内存碎片是否,何时以及如何成为问题。即使系统中实际上有很多可用内存,内存碎片最终也将导致内存耗尽。一个持续生成内存碎片的系统,无论生成的内存碎片有多小,只要时间足够长,它将耗尽内存。在许多嵌入式系统中,特别是在高可用性系统中,这种情况是不可接受的。某些软件环境(例如OSE实时操作系统)配备了可以避免内存碎片的良好工具,但是各个程序员的选择仍然会影响最终结果。 “碎片内存”描述了系统中所有不可用的空闲内存。这些资源仍未使用的原因是,负责分配内存的分配器使这些内存不可用。通常会出现此问题,因为空闲内存以较小且不连续的方式位于不同的位置。由于分配方法确定是否存在内存碎片问题,因此内存分配器在确保可用资源的可用性方面起着重要作用。在许多情况下,编译时和运行时将导致内存分配问题。程序员可以通过编译和链接程序为结构,联合,数组和标量(用作局部变量,静态变量或全局变量)中的数据分配内存。程序员还可以在运行时使用malloc()调用,例如malloc()。该命令动态分配内存。
当编译器和链接器完成内存分配功能时,将不会有内存碎片,因为编译器可以理解数据的寿命。控制可用数据寿命的优势在于,可以将数据叠加在后进先出方法上。这将使内存分配程序更有效地工作,而不会造成内存碎片。一般来说,运行时的内存分配是不可叠加的。内存分配在时间上是独立的,因此很难解决碎片问题。图1.内存碎片的几种形式。内存分配程序有三种浪费内存的基本方法:额外的开销,内部碎片和外部碎片(图1)。内存分配程序需要存储一些描述其分配状态的数据。这些存储的信息包括所有可用的内存块的位置,大小,所有权和其他内部状态详细信息通常来说,运行时分配程序存储此附加信息的最佳位置是它管理的内存。内存分配程序需要遵循一些基本的内存分配规则,例如,所有内存分配必须从4、8或16整除的地址开始(取决于处理器体系结构)内存分配程序仅将预定大小的内存块分配给客户端,并且可能还有其他原因。客户端请求一个43字节的内存块,他可能会获得44字节,48字节甚至更多的字节。所需的大小称为内部碎片。外部碎片的产生是当分配的内存块之间存在未使用的差异时,将发生外部碎片。
例如,一个应用程序分配三个连续的内存块,然后使中间内存块空闲。内存分配程序可以将中间内存块重新用于将来的分配,但是分配的块的大小不可能与总的可用内存完全相同。如果在运行期间内存分配程序没有更改其实现和舍入策略,则在系统的整个生命周期中,额外的开销和内部碎片均保持不变。尽管额外的开销和内部碎片浪费了内存,因此是不希望的,但外部碎片是嵌入式系统开发人员的真正敌人。是导致系统故障的分配问题。有几种定义内存碎片的方法,其中最常用的是:此方法适用于外部碎片,但是可以通过向分母添加内部碎片来修改此公式以包括内部碎片。内存碎片是0到1之间的分数。碎片为1(100%)的系统内存不足。如果所有可用内存都在一个内存块(最大的内存块)中,则碎片为0%。当所有可用内存的四分之一位于最大的内存块中时,碎片为75%。示例如下:系统具有5M字节的可用内存。当可分配的最大内存块为50k字节时,内存碎片为99%。这个99%的内存碎片示例来自嵌入式软实时系统开发过程中发生的实际情况。在此级别的碎片发生一秒钟后,系统崩溃了。该系统已经进行了大约两周的连续现场测试,直到碎片率达到99%。
这是由Foxit PDF Creator生成的吗?福昕软件仅用于评估。这怎么发生的?你怎么这么晚才发现当然,所有系统都经过测试,但是测试很少超过两个小时。分娩前的最终压力测试持续了一个周末。在如此短的测试周期中,不一定会发生内存碎片的后果。因此,很难回答存储器碎片达到临界值需要多长时间的问题。对于某些应用程序,在某些情况下,系统将在内存用尽之前达到稳定状态。对于其他应用程序,系统将无法及时达到稳定状态(图2)。只要消除了不确定性和风险因素,不会产生碎片的内存分配程序(图3)可以很快实现稳定)。状态,这有助于开发人员在晚上安然入睡。当开发长期不启动几个月甚至几年的操作系统时,快速收敛到稳定状态是一个重要因素。图2.此案例研究为嵌入式系统项目使用了第一个合适的内存分配程序,该系统在现场测试中连续运行了两周,然后碎片率达到了99%图3.一旦测试了整个应用程序,不产生碎片的内存分配程序就可以达到稳定状态。

很难确定哪种内存分配算法更好,因为每种算法在不同的应用中都有其自身的优势(表1)。第一种合适的内存分配算法是最常用的一种。他使用了四个指针: MSTART指向托管内存的开头; MEND指向托管内存的结尾; MBREAK指向MSTART和MEND之间的已用内存的结尾; PFREE指向第一个空闲内存块(如果有)。开始运行时,PFREE为NULL,MBREAK指向MSTART,当分配请求到来时,分配程序首先检查PFREE是否有可用的内存块,由于PFREE为NULL,所以带有请求存储量的存储块加上管理头。 MBREAK,然后更新MBREAK。重复此过程,直到系统释放一个内存块,并且管理标头包含该内存块的存储容量为止。头上的链接表插入项指向内存块,并使用指向旧PFREE内容的指针更新该块本身,以建立链接列表。下次发生分配请求时,系统将搜索空闲存储块的链接列表,以找到适合于所请求存储量的第一个存储块。可用内存块。找到合适的内存块后,他将内存块分为两部分,一部分返回到系统,另一部分返回到空闲表。
第一个合适的内存分配算法易于实现,并且一开始就非常容易使用。但是,一段时间后,将发生以下情况:当系统将内存提供给空闲表时,它将从空闲表的开头删除大内存块并插入剩余的小内存块。首次拟合算法实际上变成了排序算法,即将所有小内存碎片放在空闲列表的开头。因此,空闲列表可能变得很长,包含数百甚至数千个元素。因此,存储器分配变得非常长且不可预测,并且大存储器块的分配比小存储器块的分配花费更长的时间。此外,内存块的无限分割使内存碎片非常高。使存储器空闲时,某些实现方法会连接相邻的空闲存储器块。这种方法很有用,并且第一个合适的算法和时间共置算法(时间共置)和空间共置算法(空间共置)是不同的。当它使一个内存块空闲时,它不能增加同时空闲的相邻内存块的数量。可能性。最佳拟合和最差拟合分配程序最佳拟合算法在功能上与首次拟合算法相似。区别在于,当系统分配内存块时,它将搜索整个空闲表以查找最接近请求存储量的内存。片。该搜索比第一种合适的算法花费的时间长得多,但是分配大小存储块所需的时间没有差异。最佳拟合算法比优先拟合算法生成更多的内存碎片,因为在空闲列表的开头放置较小且不可用的碎片的排序趋势更强。
由于这个负面因素,最佳拟合算法几乎从未被采用。最不适合算法也很少使用。最适合算法的功能与最适合算法相同。不同之处在于,分配了内存块后,系统会在整个空闲表中搜索与请求的存储量不匹配的内存。此方法比最佳拟合算法快,因为它倾向于生成较小且无法使用的内存片段的趋势较弱。始终选择最大的空闲内存块并将其分成小内存块,这会增加剩余部分足够大以供系统使用的可能性。伙伴分配程序不同于本文中描述的其他分配程序。它无法根据需要从托管内存的开头创建新内存。他有一个明确的共性,即每个存储块都可以分割和组合,但不是任意的分割和组合。由Foxit PDF Creator生成?福昕软件仅用于评估。每个块都有一个朋友或“伙伴”,可以与之分离并与之结合。伙伴分配程序将存储块存储在比链表更高级的数据结构中。这些结构通常是桶,树和桩类型的组合或变体。一般来说,伙伴分配程序的工作方式很难描述,因为该技术随所选数据结构而变化。由于存在各种具有已知特征的数据结构,因此伙伴分配程序得到了广泛使用。
甚至在源代码中使用了一些合作伙伴分发程序。合作伙伴分配程序的编写通常非常复杂,其性能可能会有所不同。合作伙伴分配程序通常会在一定程度上限制内存碎片。固定存储分配程序有点像第一个空闲算法。通常有一个以上的空闲表,更重要的是,同一空闲表中的所有内存块都具有相同的存储容量。至少有四个指针:MSTART指向托管内存的开头,MEND指向托管内存的结尾,MBREAK指向MSTART和MEND之间的已用内存的结尾,而PFREE [n]指向一行所有可用内存块的指针。开头,PFREE为NULL,MBREAK指针为MSTART。当分配请求到来时,系统将请求的存储容量增加到可用存储容量之一。然后,系统检查PFREE(增加的存储量)可用存储块。因为PFREE [增加存储容量]为NULL,所以将具有存储容量加上管理标题的存储块与MBREAK分离,并更新MBREAK。重复这些步骤,直到系统释放存储块为止。此时,管理头包括存储块的存储容量。当存储块空闲时,通过标题的链接表插入项更新PFREE [对应存储量]以指向该存储块,并使用指向PFREE [对应存储的先前内容]的指针更新存储块本身。数量]以创建链接列表。
下一个分配请求到来时,系统会将PFREE [增加请求存储量]链接表的第一个存储块发送到系统。由于所有链接存储块的存储容量都相同,因此无需搜索链接列表。固定存储分配程序非常容易实现,并且至少在块存储量较小的情况下,便于计算内存碎片。但是此分配程序的局限在于,他必须分配最大的存储量。固定存储分配程序速度很快,并且可以在所有情况下保持速度。这些分配器可能会产生很多内部内存碎片,但是对于某些系统,它们的好处大于缺点。减少内存碎片内存碎片是由于在分配内存块之后将其释放而导致的,而不是将可用内存返回最大的内存块。最后一步非常关键。如果内存分配程序有效,则无法阻止系统分配内存块并使它们释放。即使内存分配程序不能确保可以将返回的内存连接到最大的内存块(此方法可以完全避免内存碎片问题),也可以设法控制和限制内存碎片。所有这些实践都涉及存储块的分段。每当系统减少分割的内存块的数量并确保分割的内存块尽可能大时,您都将得到改善。这样做的目的是尽可能多地重复使用存储块,而不是每次都划分存储块以完全满足请求的存储容量。分裂内存块会产生很多小的内存碎片,就像一堆松散的沙子一样。
将这些分散的沙子与其余的内存结合起来将非常困难。更好的方法是在每个内存块中保留一些未使用的字节。剩余多少字节取决于系统避免内存碎片的程度。对于小型系统,添加一些内部碎片字节是朝正确方向迈出的一步。当系统请求1个字节的内存时,您分配的存储量取决于系统的工作状态。如果系统分配的内存主要部分为1到16个字节,则为小型内存分配16个字节是明智的。只要您限制可以分配的最大内存块,就可以实现更大的节省。但是,此方法的缺点是系统将不断尝试分配大于限制的内存块,这可能导致系统停止工作。减少最大和最小存储块之间的存储量也很有用。使用对数增加的存储块存储可以避免大量碎片。例如,每个存储空间可能比以前的存储空间大20%。嵌入式系统中的内存分配程序在嵌入式系统中采用“一个存储容量可以满足所有需求”可能是不切实际的。就内部碎片而言,此方法的成本非常高,但是系统可以完全避免外部碎片,并达到所支持的最大存储容量。连接相邻的空闲内存块是一种可以显着减少内存碎片的技术。如果没有这种方法,将由Foxit PDF Creator生成?福昕软件仅用于评估。某些分配算法(例如第一个合适的算法)根本无法工作。

但是,效果有限。连接相邻的内存块只能缓解分配算法引起的问题,而不能解决基本问题。此外,当存储块的存储容量受到限制时,可能很难实现相邻存储块的连接。一些内存分配器非常先进,可以在运行时收集有关某个系统的分配习惯的统计信息,然后根据存储量(例如小,中和大)对所有内存分配进行分类。每次系统分配到托管内存的一个区域时,因为该区域包括此类内存块存储。根据较大的存储容量分配较小的存储容量。此方案是第一种合适的算法和一组有限的固定存储算法的有趣组合,但它不是实时的。有效使用临时限制通常非常困难,但是值得一提的是,在内存中临时扩展位于同一位置的分配程序更容易造成内存碎片。尽管其他技术可以缓解此问题,但是限制具有不同存储量的内存块的数量仍然是减少内存碎片的主要方法。现代软件环境已经实现了各种工具来避免内存碎片。例如,为分布式高可用性容错系统开发的OSE实时操作系统可以提供三个运行时内存分配程序:内核alloc(),它根据系统或内存块池进行分配;堆malloc(),根据程序堆分配; OSE内存管理程序alloc_region,他根据内存管理程序的内存进行分配。
在许多方面,Alloc是最终的内存分配程序。它生成的内存片段很少,非常快,并且具有判断功能。您可以调整甚至删除内存碎片。只有在分配了存储量并将其释放但不再分配之后,外部碎片才会发生。内部碎片将继续发生,但是对于给定的系统和八种类型的存储来说,它是恒定的。 Alloc是具有八个空闲表的固定存储分配程序的一种实现方法。系统程序员可以设置每个存储容量,并可以决定使用更少的存储空间以进一步减少碎片。除开始时以外,分配内存块和释放内存块是恒定时间的操作。首先,系统必须将请求的存储舍入到下一个可用存储。就八个存储容量而言,可以通过三个if语句实现此目标。其次,系统总是在八个空闲表的开头插入或删除存储块。开始时,分配未使用的内存需要花费更多的时间,但是速度仍然非常快,并且花费的时间是恒定的。 malloc()的内存开销(8-16字节/分配)小于alloc的内存开销,因此可以禁用内存的独占权。平均而言,malloc()分配器相当快。它的内部片段比alloc()少,但外部片段比alloc()多。他拥有最大的已分配存储空间,但是对于大多数系统而言,此限制足够大。
可选的共享所有权和低开销使malloc()适用于具有许多小对象和共享对象的C ++应用程序。堆是具有内部堆数据结构的伙伴系统的一种实现方法。在OSE中,有28种不同的可用存储容量,并且每个存储容量都是前两个存储容量的总和,因此形成了斐波那契数列。实际的存储块存储是序列号乘以16个字节,其中包括分配程序的开销或8个字节/分配(启用文件和行信息时为16个字节)。当您很少需要大块内存时,最适合使用OSE内存管理程序。典型的系统将存储空间分配给整个系统,堆或库。在具有MMU的系统中,某些实现方法使用MMU转换功能来显着减少甚至消除内存碎片。在其他情况下,OSE内存管理程序将生成很多碎片。它没有分配最大的内存量,它是第一种适用于内存分配程序的实现方法。内存分配舍入为偶数页?典型值为4 k字节。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-348297-1.html
你是美国佬的私生子吗