<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>内存 | Academic</title><link>https://loloxwg.top/tag/%E5%86%85%E5%AD%98/</link><atom:link href="https://loloxwg.top/tag/%E5%86%85%E5%AD%98/index.xml" rel="self" type="application/rss+xml"/><description>内存</description><generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><lastBuildDate>Wed, 02 Sep 2020 12:12:28 +0000</lastBuildDate><image><url>https://loloxwg.top/media/icon_hu0b7a4cb9992c9ac0e91bd28ffd38dd00_9727_512x512_fill_lanczos_center_3.png</url><title>内存</title><link>https://loloxwg.top/tag/%E5%86%85%E5%AD%98/</link></image><item><title>如何实现内存页的分配与释放</title><link>https://loloxwg.top/posts/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%86%85%E5%AD%98%E9%A1%B5%E7%9A%84%E5%88%86%E9%85%8D%E4%B8%8E%E9%87%8A%E6%94%BE/</link><pubDate>Wed, 02 Sep 2020 12:12:28 +0000</pubDate><guid>https://loloxwg.top/posts/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%86%85%E5%AD%98%E9%A1%B5%E7%9A%84%E5%88%86%E9%85%8D%E4%B8%8E%E9%87%8A%E6%94%BE/</guid><description>&lt;p>一、再梳理一下内存结构&lt;br>
1、内存memmgrob_t被划分为多个功能分区，每个功能分区用一个memarea_t描述&lt;br>
2、每个memarea_t都有一个memdivmer_t ，每个memdivmer_t 都有一个bafhlst_t数组[0-51]&lt;br>
3、每个bafhlst_t都有一个链表，用于存放内存段，规则为：&lt;br>
bafhlst_t数组中的每个bafhlst_t，会根据其在数组中的序号n，存放全部2的n次方的连续页面，也就是说：&lt;br>
第0个bafhlst_t，存放全部长度为1的内存段&lt;br>
第1个bafhlst_t，存放全部长度为2的内存段&lt;br>
第2个bafhlst_t，存放全部长度为4的内存段&lt;br>
&amp;hellip;&amp;hellip;&lt;br>
4、在内存段处理时，将开始的msadsc_t指向了最后msadsc_t结构，内存段的起止就都清晰了，而且无论首尾，都记录了分配情况，方便各类操作&lt;br>
5、所以从设计层面来讲，页面的分配和释放，也一定只会是2的n次方大小&lt;br>
&lt;br>
二、申请&lt;br>
1、根据类型找到内存区，也就是定位到了memarea_t-&amp;gt;memdivmer_t-&amp;gt;bafhlst_t数组&lt;br>
2、根据申请内存大小，用二进制1的查找，确定要至少要从bafhlst_t数组中的哪个bafhlst_t申请，才能得到足够大的内存&lt;br>
3、从第一个合适的bafhlst_t到最大的bafhlst_t，依次判断链表中有没有可用内存段，一旦有可用的内存段就使用&lt;br>
4、如果内存段大于所需，就要把多出来的内存不断除以2挂载到上一个bafhlst_t，直到达到所需长度&lt;br>
5、设置内存段状态，起始msadsc_t都标记为已占用&lt;br>
6、更新各层结构相关信息，内存申请结束&lt;br>
7、代码中还有各种加锁解锁，各种校验，还有从大到小申请的一种方式，可以看下&lt;br>
&lt;br>
三、释放&lt;br>
1、根据要释放内存段的msadsc_t，获取属于哪个内存区，也就是定位到了memarea_t-&amp;gt;memdivmer_t-&amp;gt;bafhlst_t数组&lt;br>
2、根据释放内存大小，用二进制1的查找，确定最大可以释放到bafhlst_t数组中的哪个bafhlst_t，避免内存碎片化&lt;br>
3、设置内存段状态，起始msadsc_t都标记为未使用&lt;br>
4、从找到的第一个bafhlst_t到最大的bafhlst_t，依次去看链表中有没有内存段是挨着的，如果有就合并，再去下一个bafhlst_t继续合并&lt;br>
一旦某个bafhlst_t中没能合并，就可以退出了，因为我们只存2的n次方大小的内存段&lt;br>
而且每次合并内存段后，都要清理多余的标记，而且开始的msadsc_t要指向最后的msadsc_t&lt;br>
5、把最终合并后的内存段，加入到对应的bafhlst_t中，重新设置内存段的起始msadsc_t标记&lt;br>
6、更新好各层结构相关信息，内存释放结束&lt;br>
7、代码中还有各种加锁解锁，各种校验，可以看下&lt;br>
&lt;br>
四、对于最后的问题&lt;br>
其实无论采用哪种分配方式，内存的碎片化都是难以彻底避免的。无论是操作系统、虚拟机还是应用，都要面对这个问题。业界有多种思路来解决或缓解此问题：&lt;br>
1、把不可移动内存单独管理，系统内存分区其实在一定程度上解决了这些问题&lt;br>
2、linux采用了 buddy system来缓解内存碎片问题，本节已经介绍&lt;br>
3、linux中为了处理特别小的内存请求，引入了slab技术，来辅助buddy system&lt;br>
4、windows有一种LFH技术，在程序启动时，会额外分配一定的连续内存给这个进程备用，从而降低系统层面的内存管理负担&lt;br>
5、windows在进程退出后，不会立即释放dll文件相关内存，一方面提速，一方面也缓解了操作系统内存管理负担。其实，看下你手机的APP，切换到后台时，就是这个效果&lt;br>
6、无论是linux还是windows都有低优先级进程，在后台默默做着内存规整工作，类似于磁盘碎片清理&lt;br>
7、JVM虚拟机，GC时会通过标记-整理（比如CMS）或复制-清除（比如G1）的方法来解决部分碎片问题&lt;br>
8、类似与LFH，可以考虑在内存分配时额外预留一部分，下次分配在预留的地方继续分配&lt;br>
9、为了内存规整方便，可以考虑靠近应用已分配内存区域进行分配&lt;br>
10、还有一种思路，就是将不连续的内存，转换为逻辑上连续的内存，来绕过碎片化问题，但一些情况下性能难以保证&lt;br>
&lt;br>
应用层面也有工作能做：&lt;br>
1、比如redis在处理内存的时候，申请时会额外申请一部分先备着【记得是jemalloc】，释放时也不会立即释放，有单独的线程进行处理，在应用层面去降低系统内存管理负担&lt;br>
2、同时，redis在数据结构上也做了很多努力&lt;br>
3、在写程序的时候，尽量不要零零散散的去申请大量小内存；&lt;br>
4、除了标准库以外，可以试一下 jemalloc或Doug Lea&amp;rsquo;s malloc&lt;br>
5、感兴趣可以看下redis内存管理的代码&lt;br>
额，好像跑题了。。。&lt;/p></description></item></channel></rss>