定制linux内存管理


一 malloc寻根linux下的malloc实现又叫ptmallocptmallocdlmalloc的一种变形,所以有些地方也会直接说成dlmalloc当我们调用malloc时,根据申请的大小会分成截然不同的几种处理流程。小内存会进入“定长内存”系统;大内存则进入“变长内存”系统(


备注2)一系列空闲内存链表。链表中的空闲内存块都是一样大的。

虽然定长内存系统的分配和释放极快。但是在分配大块内存时,定长内存分配会导致大量内存碎片(内碎片),关于内存碎片分类和优化我们会在后续章节再讨论。

“变长内存”系统也会组织一系列空闲内存链表。链表中的空闲块通常是不一样大的: 

变长内存会按照大小范围组织成很多个不同的链表,例如:10K-15K属于链表116K-23K属于链表224K-39K属于链表3

总体来说,变长内存的性能比定长内存要差。分配时需要遍历会导致耗时增加,而且变长内存通常需要分裂内存块,会导致外碎片增加。

二 细节决定成败从整体设计上来看,malloc做的相当好。但是当我们深入一些关键的细节时,会发现malloc针对一些特殊场景,会出现很严重的问题管理小内存块时浪费了大量的空间每个内存块需要一个size_t的空间来存放本内存块的大小,供释放时使用。以64位机器为例,size_t8字节长的。而且内存块必须在2*size_t的长度上对齐。所以当我们malloc(26)时,实际消耗的内存是48字节,浪费了45%的内存。另外内存块有一个最小大小4*size_t。在64位机器上,最小的分配单位就是32字节。当我们需要分配大量小结构体时,malloc会导致极大的内存浪费。验证代码请参考附件中的mem_little.cpp


释放内存时可能因为brk而被卡住malloc的主分配区是通过brk向内核申请内存的,非主分配区通过mmap模拟brk。而brk是一个线性的分配器,类似栈。简单来说,如果我先malloc5G内存;再malloc 16字节,抓住这16字节不释放;然后释放前面的5G内存,这5G内存是无法还给系统的。验证代码请参考附件中的mem_brk.cppmalloc的这个特性对于需要长期持有而不释放的内存是极其不友好的。而且一旦出现内存泄漏,哪怕只漏了几个字节,也可能把几个G的内存释放卡住。


低效的realloc当需要扩大已分配的内存块时,ptmalloc将尝试从尾部扩展内存块。但是当系统内存压力较大时,这个尝试的成功率是不高的,这时就需要分配一个全新的内存块并把旧数据拷贝过去,而这将引入极其巨大的性能消耗(



我们做的是一款http正向缓存代理服务器,约等于带强大缓存功能的nginx具体目标是在一台816G内存的机器上承载每秒4Gbhttp流量,100万并发连接。我们需要缓存整个互联网,所以需要上亿个10字节的结构体来存储url信息;另外还需要处理每秒4Gbhttp socket数据,并尝试进行缓存写入和读出。在连接建立时我们是不知道每个连接需要多大缓冲区的,这就需要大量的realloc来扩大已有缓冲区。当用户下载1GB的文件时,原始服务器先将1GB文件发给我们,我们再转发给用户,这将持续相当长的一段时间。在这个过程中,我们可以把已转发给用户的部分内存先释放掉,但是可惜malloc不支持部分free


在设计内存管理系统时,我们主要关心三个方面:

分配和释放效率


内存利用率方面主要是三个问题:

如果对大内存块采用定长内存系统,前后两个定长内存块之间的大小会差的较大,从而产生较大的内碎片。这也是为什么绝大多数内存管理系统,仅对小内存块使用定长内存。

外碎片:因为空闲内存块太小导致有内存又无法分配叫外碎片如下图,经过长时间malloc-free后,空闲内存(绿色)被分隔到成很多小碎片,就无法再malloc出大块内存了


无法还给系统:在上一节中已经举例说明过这个问题,free将内存还给内存管理器后可能无法归还给系统,导致其它进程分配内存失败

分配释放效率:定长分配和释放往往都是O(1)的效率

如果定长内存和变长内存交错在一起,定长内存会经常和变长内存合并,使得定长内存的优势体现不出来。


五 slab定长内存池 linux内核采用的slab算法是一个极好的定长内存管理算法

同时,这还将极大的减少定长内存和变长内存交错的情况,提升分配和释放效率。要让slab达到最高效率,我们需要事先对slab系统进行初始化,声明我们需要用到定长内存块大小(通常是某个频繁使用的struct/class),最好还能手动设定一下桶的大小。这也是为什么malloc不采用slab算法的原因。


通过支持非连续内存,我们将变长内存池的问题转化成了定长内存的问题,大内存使用过程中碰到的几个最主要问题,都已经解决了。因为所有内存块都是定长的,我们不再会有任何外碎片的问题,跑上100年也不会有外碎片。


非连续内存有一个极大的缺陷:无法用常规的strcmpmemcpy系列函数处理;也无法转换成string;使用时一不小心就会踩内存。必须提供一系列函数对非连续内存池进行处理,例如jump_get_next来获取下一个字符并自动判断是否需要跳到下一个内存块。08年阅读nginx代码时,发现nginx就已经实现了一套非连续内存池的代码,但是并没有发现任何代码在使用这个非连续内存池。对于开源这样人员多变的开发环境,确实不太适合使用非连续内存池;但是对于人员稳定性和素质有保障的商业公司,完全可以尝试用非连续内存来提升性能。


备注2实际上这里有一个lazy的机制。第一次malloc时并没有事先准备好的定长内存,此时需要从更高层的内存块中(例如largebintop),速度较慢。free时,如果大小属于fastbin会直接放入定长内存池中。如果属于smallbin,在尝试和相邻内存块合并失败时,也会放入定长内存池。在经过一段时间的mallocfree之后,如果没有触发大量的合并,我们就可以幸运的拥有一个快速的定长内存池了

应用层消耗

    Tcp协议栈

    copy_to_user

 15%    其它内核函数40%可以看到copy_to_usercopy_from_user加起来占了20%CPU。这两个内核函数属于recvsend的内核实现,负责在内核和应用层间拷贝socket数据。换句话说,如果我们通过realloc将所有socket缓冲区拷贝了一遍,就会增加约20%性能消耗<span font-size:12pt;"="" style="word-wrap: break-word;"> 

验证代码压缩包:

 mem_test.zip

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值