导航:首页 > 万维百科 > cms如何加少内存碎片

cms如何加少内存碎片

发布时间:2020-08-27 08:49:53

1、请高手指教: linux 内存优化怎么做? 就是在申请内存时怎么做到较少内存碎片??

你的情况都没说明,怎么能够让人知道症状,
我不是什么大虾,但是我知道为什么
linux支持数十种文件系统。ext3/ext2/xfs等,包括windows的NTFS和FAT等等,
而windows主要使用NTFS文件系统,兼容FAT文件系统,
你安装的时候肯定是选用了NTFS安装,导致出错,这个原因应该是硬盘的文件分区表出错,如果你用FAT文件系统安装,应该不会出错,至于原理,希望你把具体症状说出。

2、内存碎片的减少内存碎片

内存碎片是因为在分配一个内存块后,使之空闲,但不将空闲内存归还给最大内存块而产生的。最后这一步很关键。如果内存分配程序是有效的,就不能阻止系统分配内存块并使之空闲。即使一个内存分配程序不能保证返回的内存能与最大内存块相连接(这种方法可以彻底避免内存碎片问题),但你可以设法控制并限制内存碎片。所有这些作法涉及到内存块的分割。每当系统减少被分割内存块的数量,确保被分割内存块尽可能大时,你就会有所改进。
这样做的目的是尽可能多次反复使用内存块,而不要每次都对内存块进行分割,以正好符合请求的存储量。分割内存块会产生大量的小内存碎片,犹如一堆散沙。以后很难把这些散沙与其余内存结合起来。比较好的办法是让每个内存块中都留有一些未用的字节。留有多少字节应看系统要在多大程度上避免内存碎片。对小型系统来说,增加几个字节的内部碎片是朝正确方向迈出的一步。当系统请求1字节内存时,你分配的存储量取决于系统的工作状态。
如果系统分配的内存存储量的主要部分是 1 ~ 16 字节,则为小内存也分配 16 字节是明智的。只要限制可以分配的最大内存块,你就能够获得较大的节约效果。但是,这种方法的缺点是,系统会不断地尝试分配大于极限的内存块,这使系统可能会停止工作。减少最大和最小内存块存储量之间内存存储量的数量也是有用的。采用按对数增大的内存块存储量可以避免大量的碎片。例如,每个存储量可能都比前一个存储量大 20%。在嵌入式系统中采用“一种存储量符合所有需要”对于嵌入式系统中的内存分配程序来说可能是不切实际的。这种方法从内部碎片来看是代价极高的,但系统可以彻底避免外部碎片,达到支持的最大存储量。
将相邻空闲内存块连接起来是一种可以显著减少内存碎片的技术。如果没有这一方法,某些分配算法(如最先适合算法)将根本无法工作。然而,效果是有限的,将邻近内存块连接起来只能缓解由于分配算法引起的问题,而无法解决根本问题。而且,当内存块存储量有限时,相邻内存块连接可能很难实现。
有些内存分配器很先进,可以在运行时收集有关某个系统的分配习惯的统计数据,然后,按存储量将所有的内存分配进行分类,例如分为小、中和大三类。系统将每次分配指向被管理内存的一个区域,因为该区域包括这样的内存块存储量。较小存储量是根据较大存储量分配的。这种方案是最先适合算法和一组有限的固定存储量算法的一种有趣的混合,但不是实时的。
有效地利用暂时的局限性通常是很困难的,但值得一提的是,在内存中暂时扩展共处一地的分配程序更容易产生内存碎片。尽管其它技术可以减轻这一问题,但限制不同存储量内存块的数目仍是减少内存碎片的主要方法。
现代软件环境业已实现各种避免内存碎片的工具。例如,专为分布式高可用性容错系统开发的 OSE 实时操作系统可提供三种运行时内存分配程序:内核 alloc(),它根据系统或内存块池来分配;堆 malloc(),根据程序堆来分配; OSE 内存管理程序 alloc_region,它根据内存管理程序内存来分配。
从 许多方面来看,Alloc就是终极内存分配程序。它产生的内存碎片很少,速度很快,并有判定功能。你可以调整甚至去掉内存碎片。只是在分配一个存储量后,使之空闲,但不再分配时,才会产生外部碎片。内部碎片会不断产生,但对某个给定的系统和八种存储量来说是恒定不变的。
Alloc 是一种有八个自由表的固定存储量内存分配程序的实现方法。系统程序员可以对每一种存储量进行配置,并可决定采用更少的存储量来进一步减少碎片。除开始时以外,分配内存块和使内存块空闲都是恒定时间操作。首先,系统必须对请求的存储量四舍五入到下一个可用存储量。就八种存储量而言,这一目标可用三个 如果 语句来实现。其次,系统总是在八个自由表的表头插入或删除内存块。开始时,分配未使用的内存要多花几个周期的时间,但速度仍然极快,而且所花时间恒定不变。
堆 malloc() 的内存开销(8 ~ 16 字节/分配)比 alloc小,所以你可以停用内存的专用权。malloc() 分配程序平均来讲是相当快的。它的内部碎片比alloc()少,但外部碎片则比alloc()多。它有一个最大分配存储量,但对大多数系统来说,这一极限值足够大。可选的共享所有权与低开销使 malloc() 适用于有许多小型对象和共享对象的 C++ 应用程序。堆是一种具有内部堆数据结构的伙伴系统的实现方法。在 OSE 中,有 28 个不同的存储量可供使用,每种存储量都是前两种存储量之和,于是形成一个斐波那契(Fibonacci)序列。实际内存块存储量为序列数乘以 16 字节,其中包括分配程序开销或者 8 字节/分配(在文件和行信息启用的情况下为 16 字节)。
当你很少需要大块内存时,则OSE内存管理程序最适用。典型的系统要把存储空间分配给整个系统、堆或库。在有 MMU 的系统中,有些实现方法使用 MMU 的转换功能来显著降低甚至消除内存碎片。在其他情况下,OSE 内存管理程序会产生非常多的碎片。它没有最大分配存储量,而且是一种最先适合内存分配程序的实现方法。内存分配被四舍五入到页面的偶数——典型值是 4 k 字节。

3、程序中内存碎片到底是怎么产生的?

大多是内存没有释放,指针没删除。要减少就要用完后释放内存、删除指针等

4、如何解决内存碎片

下了清理软件 就搞定了

5、如何减少频繁分配内存(malloc或者new)造成的内存碎片?

一般的做法是把malloc和free包装以后使用,再加入适当的调试代码。可以参考:编写无错的C程序的秘诀的电子版。比如:free(p)包装一个函数:
void Free(void *p) {
if (p != NULL) {
free(p);
p = NULL;
}
}
至于malloc,一般的做法是分配size个字节的内存时,多分配一个调试用的结构体,然后把这些结构体做成链表,可以通过查看这个链表找到野指针。

6、怎么清理内存碎片?

内存碎片,其实和磁盘碎片不是一个级别的,一般不需要清理。

1、XP的内存是windows的,内存管理比较差,有一些程序退出后没有释放内存,有人称为内存碎片。这个可以借助第三方工具,比如smartram、内存救星之类的软件来清理释放。
2、win7以后,是使用了nt内核,内存管理十分优秀了,应用程序的内存释放机制是比较完善的,不需要管理内存碎片。这一点,从上述软件基本上没有win7版就可以看出来。因为没必要。
3、重启是最好的整理内存碎片的办法。

7、如何减少频繁分配内存造成的内存碎片

windows自带了一个磁盘碎片整理工具
打开路径:开始->所有程序->附件->系统工具->磁盘碎片整理程序
先分析磁盘碎片再进行磁盘碎片整理。

8、[编程知识]如何分配内存 内存碎片处理技术

内存碎片是一个很棘手的问题。如何分配内存决定着内存碎片是否会、何时会、如何会成为一个问题。 即使在系统中事实上仍然有许多空闲内存时,内存碎片还会最终导致出现内存用完的情况。一个不断产生内存碎片的系统,不管产生的内存碎片多么小,只要时间足够长,就会将内存用完。这种情况在许多嵌入式系统中,特别是在高可用性系统中是不可接受的。有些软件环境,如 OSE 实时操作系统已经备有避免内存碎片的良好工具,但个别程序员做出的选择仍然会对最终结果形成影响。“碎片的内存”描述一个系统中所有不可用的空闲内存。这些资源之所以仍然未被使用,是因为负责分配内存的分配器使这些内存无法使用。这一问题通常都会发生,原因在于空闲内存以小而不连续方式出现在不同的位置。由于分配方法决定内存碎片是否是一个问题,因此内存分配器在保证空闲资源可用性方面扮演着重要的角色。 编译时间与运行时间在许多情况下都会出现内存分配问题。程序员可以通过编译程序和链接程序,为结构、并集、数组和标量(用作局部变量、静态变量或全局变量)方面的数据分配内存,程序员还可以在运行时间使用诸如 malloc()调用命令动态地分配内存。当用编译程序和链接程序完成内存分配功能时,就不会出现内存碎片,因为编译程序了解数据寿命。掌握可供使用的数据寿命,好处在于可以使数据以后进先出的方式叠加起来。这样就可以使内存分配程序工作效率更高,而不会出现内存碎片。一般来说,运行时间内的内存分配是不可叠加的。内存分配在时间上是独立的,从而使得碎片问题难以解决。图1,内存碎片的几种形式。 内存分配程序浪费内存的基本方式有三种:即额外开销、内部碎片以及外部碎片(图 1)。内存分配程序需要存储一些描述其分配状态的数据。这些存储的信息包括任何一个空闲内存块的位置、大小和所有权,以及其它内部状态详情。一般来说,一个运行时间分配程序存放这些额外信息最好的地方是它管理的内存。内存分配程序需要遵循一些基本的内存分配规则。例如,所有的内存分配必须起始于可被 4、8 或 16 整除(视处理器体系结构而定)的地址。内存分配程序把仅仅预定大小的内存块分配给客户,可能还有其它原因。当某个客户请求一个 43 字节的内存块时,它可能会获得 44字节、48字节 甚至更多的字节。由所需大小四舍五入而产生的多余空间就叫内部碎片。外部碎片的产生是当已分配内存块之间出现未被使用的差额时,就会产生外部碎片。例如,一个应用程序分配三个连续的内存块,然后使中间的一个内存块空闲。内存分配程序可以重新使用中间内存块供将来进行分配,但不太可能分配的块正好与全部空闲内存一样大。倘若在运行期间,内存分配程序不改变其实现法与四舍五入策略,则额外开销和内部碎片在整个系统寿命期间保持不变。虽然额外开销和内部碎片会浪费内存,因此是不可取的,但外部碎片才是嵌入系统开发人员真正的敌人,造成系统失效的正是分配问题。定义内存碎片的方法有几种,其中最常用的是:这一方法适用于外部碎片,但可以修改这一公式使之包括内部碎片,办法是把内部碎片加入到分母中。内存碎片是一个介于 0 和 1 之间的分数。一个碎片为 1(100%)的系统就是把内存全用完了。如果所有空闲内存都在一个内存块(最大内存块)中,碎片为 0%。当所有空闲内存的四分之一在最大内存块中时,碎片为 75%。例子如下:一个系统有 5M 字节的空闲内存,当它可用来分配的最大内存块为 50 k 字节时,其内存碎片为99%。这个 99%内存碎片实例来自开发嵌入式软实时系统期间出现的一种真实情况。当这种碎片程度发生一秒后,系统就崩溃了。该系统在碎片率达到 99% 之前,已经进行了约两周的连续现场测试。这种情况是如何发生的?为什么会发现得如此晚?当然,系统都经过测试,但测试很少超过两个小时。交付前的最后压力测试持续了一个周末。在这样短的测试周期内未必会产生内存碎片的后果,所以就发生了内存碎片需要多长时间才会达到临界值,这一问题很难回答。对某些应用来说,在某些情况下,系统会在用完内存前达到一种稳定状态。而对于另一些应用来说,系统则不会及时达到稳定状态(图 2)。只要消除不确定性因素和风险因素,不产生碎片的内存分配程序(图 3)就能快速达到一种稳定状态,从而有助于开发人员夜晚安稳睡觉。在开发数月甚至数年不再重新启动的长期运行系统时,快速收敛到稳定状态是一个重要因素。在比系统连续运行周期短的时间内,对系统进行适当的测试,这是必不可少的。图2,这一案例研究把最先适合内存分配程序用于一个嵌入系统项目。系统在现场测试中连续运行了两周,然后碎片率达到 99%。图3,一个不产生碎片的内存分配程序一旦试验应用程序的全部,它就能达到稳定状态。 很难确定哪种内存分配算法更胜一筹,因为每种算法在不同的应用中各有所长(表 1)。最先适合内存分配算法是最常用的一种。它使用了四个指针:MSTART 指向被管理内存的始端;MEND 指向被管理内存的末尾;MBREAK 指向 MSTART 和 MEND 之间已用内存的末端; PFREE 则指向第一个空闲内存块(如果有的话)。在系统开始运行时,PFREE 为 NULL,MBREAK 指向 MSTART。当一个分配请求来到时,分配程序首先检查 PFREE有无空闲内存块。由于 PFREE 为 NULL,一个具有所请求存储量加上管理标题的内存块就脱离 MBREAK ,然后MBREAK就更新。这一过程反复进行,直至系统使一个内存块空闲,管理标题包含有该存储块的存储量为止。此时,PFREE 通过头上的链接表插入项被更新为指向该内存块,而块本身则用一个指向旧 PFREE 内容的指针进行更新,以建立一个链接表。下一次出现分配请求时,系统就会搜索空闲内存块链接表,寻找适合请求存储量的第一个空闲内存块。一旦找到合适的内存块,它将此内存块分成两部分,一部分返还给系统,另一部分则送回给自由表。最先适合内存分配算法实现起来简单,而且开始时很好用。但是,经过一段时间后,会出现如下的情况:当系统将内存交给自由表时,它会从自由表的开头部分去掉大内存块,插入剩余的小内存块。最先适合算法实际上成了一个排序算法,即把所有小内存碎片放在自由表的开头部分。因此,自由表会变得很长,有几百甚至几千个元素。因此,内存分配变得时间很长又无法预测,大内存块分配所花时间要比小内存块分配来得长。另外,内存块的无限制拆分使内存碎片程度很高。有些实现方法在使内存空闲时会将邻近的空闲内存块连接起来。这种方法多少有些作用,而最先适合算法与时间共处算法(time co-location)和空间共处算法(spatial co-location)不同,它在使内存块空闲时,无法提高相邻内存块同时空闲的概率。 最佳适合与最差适合分配程序 最佳适合算法在功能上与最先适合算法类似,不同之处是,系统在分配一个内存块时,要搜索整个自由表,寻找最接近请求存储量的内存块。这种搜索所花的时间要比最先适合算法长得多,但不存在分配大小内存块所需时间的差异。最佳适合算法产生的内存碎片要比最先适合算法多,因为将小而不能使用的碎片放在自由表开头部分的排序趋势更为强烈。由于这一消极因素,最佳适合算法几乎从来没有人采用过。最差适合算法也很少采用。最差适合算法的功能与最佳适合算法相同,不同之处是,当分配一个内存块时,系统在整个自由表中搜索与请求存储量不匹配的内存快。这种方法比最佳适合算法速度快,因为它产生微小而又不能使用的内存碎片的倾向较弱。始终选择最大空闲内存块,再将其分为小内存块,这样就能提高剩余部分大得足以供系统使用的概率。伙伴(buddy)分配程序与本文描述的其它分配程序不同,它不能根据需要从被管理内存的开头部分创建新内存。它有明确的共性,就是各个内存块可分可合,但不是任意的分与合。每个块都有个朋友,或叫“伙伴”,既可与之分开,又可与之结合。伙伴分配程序把内存块存放在比链接表更先进的数据结构中。这些结构常常是桶型、树型和堆型的组合或变种。一般来说,伙伴分配程序的工作方式是难以描述的,因为这种技术随所选数据结构的不同而各异。由于有各种各样的具有已知特性的数据结构可供使用,所以伙伴分配程序得到广泛应用。有些伙伴分配程序甚至用在源码中。伙伴分配程序编写起来常常很复杂,其性能可能各不相同。伙伴分配程序通常在某种程度上限制内存碎片。固定存储量分配程序有点像最先空闲算法。通常有一个以上的自由表,而且更重要的是,同一自由表中的所有内存块的存储量都相同。至少有四个指针:MSTART 指向被管理内存的起点,MEND 指向被管理内存的末端,MBREAK 指向 MSTART 与 MEND 之间已用内存的末端,而 PFREE[n] 则是指向任何空闲内存块的一排指针。在开始时,PFREE[*] 为 NULL,MBREAK 指针为 MSTART。当一个分配请求到来时,系统将请求的存储量增加到可用存储量之一。然后,系统检查 PFREE[ 增大后的存储量 ] 空闲内存块。因为 PFREE[ 增大后的存储量 ] 为 NULL,一个具有该存储量加上一个管理标题的内存块就脱离 MBREAK,MBREAK 被更新。这些步骤反复进行,直至系统使一个内存块空闲为止,此时管理标题包含有该内存块的存储量。当有一内存块空闲时,PFREE[ 相应存储量 ] 通过标题的链接表插入项更新为指向该内存块,而该内存块本身则用一个指向 PFREE[ 相应存储量 ] 以前内容的指针来更新,以建立一个链接表。下一次分配请求到来时,系统将 PFREE[ 增大的请求存储量 ] 链接表的第一个内存块送给系统。没有理由搜索链接表,因为所有链接的内存块的存储量都是相同的。固定存储量分配程序很容易实现,而且便于计算内存碎片,至少在块存储量的数量较少时是这样。但这种分配程序的局限性在于要有一个它可以分配的最大存储量。固定存储量分配程序速度快,并可在任何状况下保持速度。这些分配程序可能会产生大量的内部内存碎片,但对某些系统而言,它们的优点会超过缺点。 减少内存碎片 内存碎片是因为在分配一个内存块后,使之空闲,但不将空闲内存归还给最大内存块而产生的。最后这一步很关键。如果内存分配程序是有效的,就不能阻止系统分配内存块并使之空闲。即使一个内存分配程序不能保证返回的内存能与最大内存块相连接(这种方法可以彻底避免内存碎片问题),但你可以设法控制并限制内存碎片。所有这些作法涉及到内存块的分割。每当系统减少被分割内存块的数量,确保被分割内存块尽可能大时,你就会有所改进。这样做的目的是尽可能多次反复使用内存块,而不要每次都对内存块进行分割,以正好符合请求的存储量。分割内存块会产生大量的小内存碎片,犹如一堆散沙。以后很难把这些散沙与其余内存结合起来。比较好的办法是让每个内存块中都留有一些未用的字节。留有多少字节应看系统要在多大程度上避免内存碎片。对小型系统来说,增加几个字节的内部碎片是朝正确方向迈出的一步。当系统请求1字节内存时,你分配的存储量取决于系统的工作状态。如果系统分配的内存存储量的主要部分是 1 ~ 16 字节,则为小内存也分配 16 字节是明智的。只要限制可以分配的最大内存块,你就能够获得较大的节约效果。但是,这种方法的缺点是,系统会不断地尝试分配大于极限的内存块,这使系统可能会停止工作。减少最大和最小内存块存储量之间内存存储量的数量也是有用的。采用按对数增大的内存块存储量可以避免大量的碎片。例如,每个存储量可能都比前一个存储量大 20%。在嵌入式系统中采用“一种存储量符合所有需要”对于嵌入式系统中的内存分配程序来说可能是不切实际的。这种方法从内部碎片来看是代价极高的,但系统可以彻底避免外部碎片,达到支持的最大存储量。将相邻空闲内存块连接起来是一种可以显著减少内存碎片的技术。如果没有这一方法,某些分配算法(如最先适合算法)将根本无法工作。然而,效果是有限的,将邻近内存块连接起来只能缓解由于分配算法引起的问题,而无法解决根本问题。而且,当内存块存储量有限时,相邻内存块连接可能很难实现。有些内存分配器很先进,可以在运行时收集有关某个系统的分配习惯的统计数据,然后,按存储量将所有的内存分配进行分类,例如分为小、中和大三类。系统将每次分配指向被管理内存的一个区域,因为该区域包括这样的内存块存储量。较小存储量是根据较大存储量分配的。这种方案是最先适合算法和一组有限的固定存储量算法的一种有趣的混合,但不是实时的。有效地利用暂时的局限性通常是很困难的,但值得一提的是,在内存中暂时扩展共处一地的分配程序更容易产生内存碎片。尽管其它技术可以减轻这一问题,但限制不同存储量内存块的数目仍是减少内存碎片的主要方法。现代软件环境业已实现各种避免内存碎片的工具。例如,专为分布式高可用性容错系统开发的 OSE 实时操作系统可提供三种运行时内存分配程序:内核 alloc(),它根据系统或内存块池来分配;堆 malloc(),根据程序堆来分配; OSE 内存管理程序 alloc_region,它根据内存管理程序内存来分配。从 许多方面来看,Alloc就是终极内存分配程序。它产生的内存碎片很少,速度很快,并有判定功能。你可以调整甚至去掉内存碎片。只是在分配一个存储量后,使之空闲,但不再分配时,才会产生外部碎片。内部碎片会不断产生,但对某个给定的系统和八种存储量来说是恒定不变的。Alloc 是一种有八个自由表的固定存储量内存分配程序的实现方法。系统程序员可以对每一种存储量进行配置,并可决定采用更少的存储量来进一步减少碎片。除开始时以外,分配内存块和使内存块空闲都是恒定时间操作。首先,系统必须对请求的存储量四舍五入到下一个可用存储量。就八种存储量而言,这一目标可用三个 如果 语句来实现。其次,系统总是在八个自由表的表头插入或删除内存块。开始时,分配未使用的内存要多花几个周期的时间,但速度仍然极快,而且所花时间恒定不变。堆malloc() 的内存开销(8 ~ 16 字节/分配)比 alloc小,所以你可以停用内存的专用权。malloc() 分配程序平均来讲是相当快的。它的内部碎片比alloc()少,但外部碎片则比alloc()多。它有一个最大分配存储量,但对大多数系统来说,这一极限值足够大。可选的共享所有权与低开销使 malloc() 适用于有许多小型对象和共享对象的 C++ 应用程序。堆是一种具有内部堆数据结构的伙伴系统的实现方法。在 OSE 中,有 28 个不同的存储量可供使用,每种存储量都是前两种存储量之和,于是形成一个斐波那契(Fibonacci)序列。实际内存块存储量为序列数乘以 16 字节,其中包括分配程序开销或者 8 字节/分配(在文件和行信息启用的情况下为 16 字节)。当你很少需要大块内存时,则OSE内存管理程序最适用。典型的系统要把存储空间分配给整个系统、堆或库。在有 MMU 的系统中,有些实现方法使用 MMU 的转换功能来显著降低甚至消除内存碎片。在其他情况下,OSE 内存管理程序会产生非常多的碎片。它没有最大分配存储量,而且是一种最先适合内存分配程序的实现方法。内存分配被四舍五入到页面的偶数——典型值是 4 k 字节。(T111)

9、怎样设计一个内存池,减少内存碎片?

一般工程里不推荐你写,因为你费力写一个出来99%可能性没有内置的好,且内存出bug难调试
一定要设计的话,你也可以当个玩具写写玩玩:

1. 实现教科书上的内存分配器:
做一个链表指向空闲内存,分配就是取出一块来,改写链表,返回,释放就是放回到链表里面,并做好归并。注意做好标记和保护,避免二次释放,还可以花点力气在如何查找最适合大小的内存快的搜索上,减少内存碎片,有空你了还可以把链表换成伙伴算法,写着玩嘛。

2. 实现固定内存分配器:
即实现一个 FreeList,每个 FreeList 用于分配固定大小的内存块,比如用于分配 32字节对象的固定内存分配器,之类的。每个固定内存分配器里面有两个链表,OpenList 用于存储未分配的空闲对象,CloseList用于存储已分配的内存对象,那么所谓的分配就是从 OpenList 中取出一个对象放到 CloseList 里并且返回给用户,释放又是从 CloseList 移回到 OpenList。分配时如果不够,那么就需要增长 OpenList:申请一个大一点的内存块,切割成比如 64 个相同大小的对象添加到 OpenList中。这个固定内存分配器回收的时候,统一把先前向系统申请的内存块全部还给系统。

3. 实现 FreeList 池:
在你实现了 FreeList的基础上,按照不同对象大小(8字节,16字节,32,64,128,256,512,1K。。。64K),构造十多个固定内存分配器,分配内存时根据内存大小查表,决定到底由哪个分配器负责,分配后要在头部的 header 处(ptr[-sizeof(char*)]处)写上 cookie,表示又哪个分配器分配的,这样释放时候你才能正确归还。如果大于64K,则直接用系统的 malloc作为分配,如此以浪费内存为代价你得到了一个分配时间近似O(1)的内存分配器,差不多实现了一个 memcached 的 slab 内存管理器了,但是先别得意。此 slab 非彼 slab(sunos/solaris/linux kernel 的 slab)。这说白了还是一个弱智的 freelist 无法归还内存给操作系统,某个 FreeList 如果高峰期占用了大量内存即使后面不用,也无法支援到其他内存不够的 FreeList,所以我们做的这个和 memcached 类似的分配器其实是比较残缺的,你还需要往下继续优化。

4. 实现正统的 slab (非memcached的伪 slab)代替 FreeList:
这时候你需要阅读一下论文了,现代内存分配技术的基础,如何管理 slab 上的对象,如何进行地址管理,如何管理不同 slab 的生命周期,如何将内存回收给系统。然后开始实现一个类似的东西,文章上传统的 slab 的各种基础概念虽然今天没有改变,但是所用到的数据结构和控制方法其实已经有很多更好的方法了,你可以边实现边思考下,实在不行还可以参考 kernel 源码嘛。但是有很多事情应用程序做不了,有很多实现你是不能照搬的,比如页面提供器,可以提供连续线性地址的页面,再比如说 kernel 本身记录着每个页面对应的 slab,你查找 slab 时,系统其实是根据线性地址移位得到页面编号,然后查表得到的,而你应用程序不可能这么干,你还得做一些额外的体系来解决这些问题,还需要写一些额外的 cookie 来做标记。做好内存收缩工作,内存不够时先收缩所有分配器的 slab,再尝试重新分配。再做好内存回收工作,多余的内存,一段时间不使用可以还给操作系统。

5. 实现混合分配策略:
你实现了上面很多常见的算法后,该具体阅读各种内存分配器的代码了,这些都是经过实践检验的,比如 libc 的内存分配器,或者参考有自带内存管理的各种开源项目,比如 python 源码,做点实验对比他们的优劣,然后根据分配对象的大小采用不同的分配策略,区别对待各种情况。试验的差不多了就得引入多线程支持了,将你的锁改小。注意很多系统层的线程安全策略你是没法弄的,比如操作系统可以关中断,短时间内禁止本cpu发生任务切换,这点应用程序就很麻烦了,还得用更小的锁来代替。当锁已经小到不能再小,也可以选择引入 STM 来代替各种链表的锁。

6. 实现 Per-CPU Cache:
现代内存分配器,在多核下的一个重要优化就是给多核增加 cache,为了进一步避免多线程锁竞争,需要引入 Per-CPU Cache 了。分配内存先找到对应线程所在的cpu,从该cpu上对应的 cache 里分配,cache 不够了就一次性从你底层的内存分配器里多分配几个对象进来填充 cache,释放时也是先放回 cache,cache里面如果对象太多,就做一次收缩,把内存换个底层分配器,让其他 cpu 的cache有机会利用。这样针对很多短生命周期的频繁的分配、释放,其实都是在 cache 里完成的,没有锁竞争,同时cache分配逻辑简单,速度更快。操作系统里面的代码经常是直接读取当前的cpu是哪个,而应用层实现你可以用 thread local storage 来代替,目前这些东西在 crt的 malloc 里还暂时支持不到位(不排除未来版本会增加),可以更多参考 tc/jemalloc。

7. 实现地址着色:
现代内存分配器必须多考虑总线压力,在很多机型上,如果内存访问集中在某条 cache line相同的偏移上,会给总线带来额外的负担和压力。比如你经常要分配一个 FILE 对象,而每个 FILE对象使用时会比较集中的访问 int FILE::flag; 这个成员变量,如果你的页面提供器提供的页面地址是按照 4K对齐的,那么很可能多个 FILE对象的 flag 成员所处的 cache line 偏移地址是相同的,大量访问这些相同的偏移地址会给总线带来很大负担,这时候你需要给每个对象额外增加一些偏移,让他们能够均匀的分布在线性地址对应的cache line 偏移上,消减总线冲突的开销。

8. 优化缓存竞争:
多核时代,很多单核时代的代码都需要针对性的优化改写,最基本的一条就是 cache 竞争,这是比前面锁竞争更恶劣的情况:如果两个cpu同时访问相同的 cache-line 或者物理页面,那么 cpu 之间为了保证内存一致性会做很多的通信工作,比如那个cpu0需要用到这段内存,发现cpu1也在用,那么需要通知cpu1,将cpu1 L1-L2缓存里面的数据写回该物理内存,并且释放控制权,这时cpu0取得了控制权才能继续操作,期间cpu0-cpu1之间的通信协议是比较复杂的,代价也是比较大的,cache竞争比锁竞争恶劣不少。为了避免 cache 竞争,需要比先前Per-CPU cache 更彻底的 Per-CPU Page 机制来解决,直接让不同的cpu使用不同的页面进行二次分配,彻底避免 cache 竞争。具体应用层的做法也是利用线性地址来判断所属页面(因为物理页面映射到进程地址也是4k对齐的),同时继续使用 thread local storage 或者用系统提供的 api 读取当前属于哪个 cpu 来实现。为了避免核太多每个核占据大量的页面带来的不必要的浪费,你可以参考下 Linux 最新的 slub 内存分配算法,但是 slub 也有未尽之处,好几个 linux 发行版在实践中发现 slub 还是存在一些问题的(非bug,而是机制),所以大部分发行版默认都是关闭 slub 的,虽然,你还是可以借鉴测试一下。

9. 调试和折腾:
继续参考各种现代内存分配器,取长补短,然后给你的分配器添加一些便于调试的机制,方便诊断各种问题。在你借鉴了很多开源项目,自己也做了一些所谓的优化,折腾了那么久以后,你或许以为你的分配器可以同各种开源分配器一战了,测试效果好像也挺好的,先别急,继续观察内存利用率,向操作系统申请/归还内存的频率等一系列容易被人忽视的指标是否相同。同时更换你的测试用例,看看更多的情况下,是否结果还和先前一样?这些都差不多的时候,你发现没有个一两年的大规模持续使用,你很难发现一些潜在的隐患和bug,可能你觉得没问题的代码,跑了两年后都会继续报bug,这很正常,多点耐心,兴许第三年以后就比较稳定了呢?

10、phpcms如何添加碎片

在模板中直接插入 {block('news', 1)} 格式的碎片标签,然后进后台模板管理点击“可视化”即可看到添加碎片的链接。

碎片参数说明:
1、第一个参数“news”是页面唯一标识,您可以自己命名;
2、第二个参数“1”是当前页面的碎片序号,为正整数,保证同一页面不重复即可

如何新建碎片
1、选中增加碎片的模板,在要添加的位置写上类似如下{pc:block pos="chuangxinji_index_left"}{/pc}的语句。 "chuangxinji_index_left" 为页面的标识,第二个参数可以省略。

2、在后台的内容-》内容发布管理-》碎片管理,点击下内容页(如果添加的碎片在内容页,就点击内容页,如果在首页,就点击首页)。在新跳转的页面中,在添加碎片的位置会出现“碎片:碎片名” 。点击这个碎片,点击 添加碎片,在碎片配置好后点击确定。

3、刷新一下,在碎片管理里,会出现设置好的碎片 chuangxinji_index_left ,点击“维护内容”那个图标,然后在编辑框里对碎片进行编辑。

与cms如何加少内存碎片相关的知识