从文章中的补丁可以看出,在这之前,内存分配子结构体中是没有current_free_index和max_free_index这两个成员的。并且,那时的apache在内存分配子未被释放之前是不会释放内存分配子中保留的内存。而之所以加入这两个成员,目的就是为了控制内存分配子中保管内存的数量。至此,可以基本断定,current_free_index和max_free_index是协同工作来控制分配子中的内存。
呵呵,其实刚才说的是表面现象,apr靠这样一种机制保证了,分配子只在内存中占有max_free_index这么多的空间,如果你向系统申请了超过这么的空间,apr只把不超过的部分留在自己的free链表中,超过的部分统统还给系统。
这就是靠我刚才说的使用current_free_index这样一个机制来实现的,呵呵。
至此,current_free_index的功能已经基本有了眉目。
而tingya在文章中对max_free_index的作用概括为:“如果结点的大小超过了完全释放的阙值max_free_index,那么我们就不能将其简单的归还到索引链表中,而必须将其完全归还给操作系统。”
但是在通过阅读代码之后,可以发现,二人的说法虽然各有一定道理,但是对current_free_index和max_free_index的功能概括却并不准确。我在这里试着对这两个成员的功能作一个更加准确的概括:
max_free_index用于记录内存分配子中最多可以容纳的内存空间大小。
current_free_index用于记录内存分配子中当前可以继续容纳的内存空间的大小。
其实,allan已经基本对这两个功能进行了描述,但是current_free_index并非仅仅如他所说的,“是当前应该释放的index值(超过这个值就应该释放)”
这两个变量控制了内存分配子内容量的大小。这样做是基于以下的事实:
内存分配子中保存的都是从操作系统申请来的,但是至今仍是闲置的内存空间。这些空间是以多个链表的形式保存的。这样的空间不能太大——否则大量的系统资源被占用却未被利用,影响了系统的效率;同时,这样的空间又不能太小——否则频繁地从操作系统内申请和释放内存同样会影响系统的效率。因此,内存分配子中保存的这些闲置内存的数量就要控制在一定范围之内。这样,通过max_free_index来记录内存分配子中最多可以容纳的内存空间大小。在调用apr_allocator_free函数释放一个分配子节点到分配子中时,会检查分配子中当前内存数,加上这个即将被释放的分配子节点携带的内存空间数之和是否超过了max_free_index。而因为current_free_index记录了内存分配子中当前可以继续容纳的内存空间大小,那么,只要检测这个即将释放的分配子节点携带的内存空间数是否大于current_free_index就好了。这个检测是通过判断 index > current_free_index 的值来完成的。
4. 关于current_free_index溢出的问题
在google的时候,我同样发现了这样的一篇文章:
其中提到了:
We probably should have a check in apr_allocator_free() to
make sure that we're not causing an unsigned int overflow in
allocator->current_free_index, e.g.,
+ if (index > current_tree_index) {
+ current_tree_index = 0;
+ }
+ else {
current_free_index -= index;
+ }
按照如此说法,current_free_index对于溢出的检测的确存在问题。但是就此问题我也做了一个粗略的分析。
(1) 当max_free_index不等于0时
即,对于内存分配子中的内存大小作了限制。这时,当释放一个节点时,会执行以下if语句:
if (max_free_index != APR_ALLOCATOR_MAX_FREE_UNLIMITED
&& index > current_free_index) {
node->next = freelist;
freelist = node;
}
因为已经假设max_free_index不等于零,即,判断语句的前半部分为真,如果index > current_free_index,则会将分配子节点释放给操作系统而不对current_free_index进行修改。一旦index > current_free_index为假,那么执行current_free_index -= index也不会发生溢出,因为current_free_index - index >= 0。
(2) 当max_free_index等于0时
即,对于内存分配子中内存大小不做限制。那么很显然,既然不做限制,current_free_index也便没有了任何作用,它的溢出也就无所谓了。而如果在current_free_index溢出之后再通过apr_allocator_max_free_set调整回收门槛,由于current_free_index溢出变成了一个很大的整数,加上即将释放的节点的index后可能依然大于max_free_index,但是由于下面语句的存在:
if (allocator->current_free_index > max_free_index)
allocator->current_free_index = max_free_index;
所以current_free_index值会被设成max_free_index的值。
但是仍然存在一个问题:
假如通过apr_allocator_create创建了一个无回收门槛的分配子allocator,调用apr_allocator_alloc在allocator中申请了一个100K的节点。之后,通过调用apr_allocator_alloc将这个节点释放给allocator。此时,allocator->current_free_index已经溢出变为一个很大的正整数。最后再通过apr_allocator_max_free_set调整回收门槛为50K。调用结束,current_free_index和max_free_index值会相等,都表示与50K对应的索引值。但是,此时内存分配子中却存储着一个100K的节点。这样,current_free_index就不再表示当前分配子可以继续存储的内存空间大小。除非用户的再次申请大于或者等于100K的空间,否则分配子中的节点索引的和会大于max_free_index值。不过,这种调用方法(即,先申请 释放内存后再设置门槛)可以比较容易地避免。因此,这种内存浪费的问题也便不是很明显了。
注: 本人十一长假期间在家,手头的电脑不是自己的,只有windows系统且无开发环境,所以无法做相关的测试。因此所说的一些话仅仅是臆断,不能保证正确性。还望有相关测试条件的人员帮忙替我做完这个测试,特别是对我最后提出的那个疑问的测试。文章中不正确的地方,很可能是我自身疏忽的缘故,还望多多谅解。
5. 溢出问题的解决
关于解决上述的current_free_index溢出问题,存在着两种方法(别着急,先别急着改代码,一会我会说原因)。
第一种,就是如第4节开头部分的补丁所示,修改allocator_free函数,在减少current_free_index前对减数和被减数进行检测,避免current_free_index溢出。
第二种,就是修改apr_allocator_max_free_set,在其中加入释放多余空间的功能。也就是说,如果max_free_index设置的值表示为a,则将内存分配子中的节点释放直到其中的总大小小于等于a为止。并调整相应的current_free_index值。
比较起来,第一种方案似乎容易实现些。
但是,为什么apache项目组在05年收到补丁后却迟迟不肯修改自己的代码呢?我个人认为是有以下原因的:
1. current_free_index溢出的可能性不是很大。正如我在前面一节说的,它的溢出只有在特定的调用顺序和参数的前提下才会发生,而且会随着下次调用apr_allocator_alloc而逐渐消除溢出对系统的影响。
2.apr_allocator_free这个函数调用的频率相对较高,它执行的效率直接影响着整个系统的效率。如果要在其中加入什么代码,必须要经过深思熟虑——哪怕是一个简单的if-else。
3. 正如前面一节所说的,current_free_index溢出是在特定的函数调用顺序和参数的情况下才发生。概括起来,可以说是在申请并释放大量内存后,调用apr_allocator_max_free_set,将本是无回收门槛的分配子设置上门槛。但是,凭借常识可以知道,在一系列大量申请并释放内存后,系统往往会逐渐地恢复到申请小片内存。换句话说,一般的系统,很少在申请大量内存过后,突然间改变成仅仅申请少量的内存而不再申请大片内存。这之间往往会存在一个过渡期,因此,在这个过渡期中,在分配子里面存储一些过剩的内存空间以备不时之需往往是有必要的。况且,这种内存溢出带来的问题可以在调用一次或几次apr_allocator_alloc之后得到消除。如果在调用apr_allocator_max_free_set时便一下子把内存分配子中的节点释放到相应的数量,那么既消耗了系统资源(因为多次调用了free,把节点占用的内存还给操作系统),又不能应付以后可能发生的过渡时期。因此,这么做显然不是非常理智。
如前所述,消除溢出的方法的确存在,但是比起现在的做法,不如让溢出继续保留——反正这种溢出发生的可能性不是很大,而且非常容易避免。
最后,感谢tingya对apache代码的分析,我受益匪浅。
感谢allan对current_free_index的总结。
感谢各位网友的积极讨论。
相关推荐
内存分配子中的`current_free_index`成员与内存池中的同名变量有着类似但更具体的用途。 **成员作用:** - **细化空闲内存定位:** 在内存分配子中,`current_free_index`帮助定位特定分配子管理的空闲内存起始位置...
在企业运营过程中,确保团队成员能够访问特定的系统和数据是至关重要的。"如何给员工分配子账户-登录系统查询.ppt"这个文档提供了一种方法来指导管理员为员工分配子账户并允许他们登录系统进行查询。以下是详细的...
减数分裂使得配子中的染色体数目减半,而受精作用则将两个配子的染色体组合在一起,恢复到体细胞的染色体数目。 3. 精原细胞减数分裂顺序:减数分裂I包括前期I、中期I、后期I和末期I,接着是减数分裂II,包括前期II...
例如,在查找操作中,如果知道子字符串长度远小于主字符串,可以只分配子字符串的长度,从而减少不必要的内存开销。 此外,还可以通过预估可能的最大需求来一次性分配内存,避免频繁的`realloc()`操作,因为`...
在本研究中,SERS被用来研究朊蛋白与适配子的相互作用,通过分析信号强度变化,可以定量评估这种相互作用。 7. **荧光策略**:第三章介绍了基于双适配子的金属增强荧光策略,这可能是另一种检测朊蛋白的方法。这种...
生物芯片技术是现代生命科学领域中一个重要的研究方向,它在高通量快速检测生物靶分子方面具有重要作用。生物芯片利用固相载体表面构建微型生物化学分析系统,实现分子生物信息研究的工程化、集约化、高效化和自动化...
长字符串中匹配子字符串
该研究通过在烟草雌配子体内表达含有GFP标记的微丝骨架,为揭示微丝骨架在被子植物双受精过程中的功能和作用机制提供了重要的研究工具和基础。例如,研究者可以实时观察微丝骨架在受精过程中的变化,以及它与花粉管...
分裂图及配子种类 分裂图是生物学中用于描述细胞分裂过程的图形表示法。它可以帮助研究人员了解细胞分裂的机理、染色体的排列方式和配子的形成过程。在这里,我们将围绕分裂图及配子种类这两个概念,探讨相关的知识...
高中生物:减数分裂与配子形成教案中图版必修2.pdf
matlab时空匹配核心子函数bilinear
在本案例中,适配子可能是为了识别和结合结核分枝杆菌H37Rv的特定DNA序列,从而在基因检测、分子诊断或者药物靶向等方面发挥作用。适配子的制备通常包括设计、合成、筛选和优化等步骤,以确保其特异性和亲和力。 ...
适配子微流体芯片技术在肿瘤学领域的应用是一个前沿研究方向,该技术在鼻咽癌的诊断中展现了潜在的临床应用价值。鼻咽癌,作为一种发生于鼻咽部位的恶性肿瘤,在我国南方较为常见。由于早期症状不明显且病变位置较...
高中生物:减数分裂与配子形成教案中图版必修2-7页.pdf
在这个过程中,配子是减数分裂的产物,包括精子和卵细胞。在分析配子类型时,我们需要考虑染色体的数量、同源染色体的配对以及基因的组合。 首先,对于具有8条染色体的生物,其性原细胞经过减数分裂会产生4个精子或...
【减数分裂】是生物进行有性生殖时,形成配子(精子或卵细胞)时特有的细胞分裂方式。与有丝分裂不同,减数分裂过程中染色体仅复制一次,但细胞却连续分裂两次,导致最终形成的成熟生殖细胞中染色体数目减少一半。这...
- 在减数分裂过程中,基因的分离遵循孟德尔的基因分离定律,同源染色体上的等位基因会随机分配到不同的配子中。 - 自由组合定律则描述了非同源染色体上的基因组合是独立的,所以不同染色体上的基因可以自由组合,...
在给定的文件中,研究人员详细介绍了通过一种名为SELEX的技术筛选出一种能够特异性识别无乳链球菌的DNA适配子,这一发现对于无乳链球菌的检测和研究具有重要意义。 首先,了解SELEX技术是基础。SELEX,全称为...
【配子的形成与受精】是生物学中的关键概念,主要涉及植物的生殖过程。在这个过程中,雌配子体的形成始于雌蕊内的胚珠,胚珠中的胚囊母细胞通过减数分裂产生四个大孢子,其中三个会自然解体,留下一个继续发育。这个...