如何设计一个内存分配器

作者&投稿:机饼 (若有异议请与网页底部的电邮联系)
如何设计一个内存分配器~

通常工程里不推荐自己写内存分配器,因为你费力写一个出来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:


这时候你需要阅读一下 http://citeseer.ist.psu.edu/bonwick94slab.html 这篇论文了,现代内存分配技术的基础,如何管理 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,这很正常,多点耐心,兴许第三年以后就比较稳定了呢?



有卯用呢?


十多年前 libc 还不成熟的情况下,为了程序长时间运行的稳定性,大部分程序员都必须针对自己的应用来实现针对特定情况的内存分配器。当年如果不自己管理内存,很多客户端,如果计算密集频繁分配,才开始可能没什么区别,但跑个几个小时性能立马就下降下来了;服务器进程持续运行个10多天不重启,速度也会越来越慢,碎片多了嘛。如今 libc 的 malloc 也进步了很多,这样的情况比较少了,那你再做一个内存池的意义何在呢?

在你的玩具比较稳定的情况下,终于可以产生一些价值了,因为一些性能指标你无法兼得,标准的分配器往往提供了一个类似保守和中庸的做法,来针对大部分的情况,你可以做的第一步,就是打破这样的平衡,让你的分配器倾向于某些情况比如:

1. 现代计算机内存都很大,你是不是可以牺牲内存利用率为代价换取更高的内存归还/重用的效率?同时换取更快的分配速度?或许你会发现,你可以比 libc 的 malloc 平均浪费 30%内存的代价换来两倍以上的性能提升,在一些内存分配成为瓶颈的应用中起到积极的作用。

2. 比如你可以调整大小内存的比值,libc如果认为 8K以下是小内存,那么你可以不那么认为。

3. 比如如果你的系统就是一个单线程的东西,那么你是否能提供开关,完全以单线程的模式进行运作,完全绕过各种锁和针对多核进行的各种冗余操作呢?

4. 比如你的机器内存有限,你应用需要耗费大量的内存,那么你可以引入其他机制,以牺牲少量性能为代价,换取更好的内存回收效果和内存利用率。

5. 最近分配的内存在线性地址上能否尽量连续?能否在地址上尽量规避缺页中断?

6. 比如你程序里面某些对象需要被跟踪,你能否直接在分配器上实现对象跟踪机制,跟踪各种泄漏,越界问题?

7. 每个内存分配都在寻求最佳的公平,你在乎的公平是什么?

模块主要由以下部分组成。
ObjectPool 对象池
MemPool 内存池
FastMemAlloc : 高速的大内存分配
FastSmallAlloc : 快速的小内存分配。
FixMemAlloc : 小浪费空间的大内存分配。
FixSmallMemAlloc : 小浪费空间的小内存分配。基本不浪费空间。
MemState。具有统计内存状态。(dumpMemState)和检测内存越界dumpBound。

除了FastMemAlloc外,所有的分配器内存都由MemState记录。 FastMemAlloc的内存状态由他自己记录,你可以调用FastMemAlloc::diagnostic()和FastMemAlloc::dumpBound()来分析内存。

通常工程里不推荐自己写内存分配器,因为你费力写一个出来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:

这时候你需要阅读一下 http://citeseer.ist.psu.edu/bonwick94slab.html 这篇论文了,现代内存分配技术的基础,如何管理 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,这很正常,多点耐心,兴许第三年以后就比较稳定了呢?

有卯用呢?

十多年前 libc 还不成熟的情况下,为了程序长时间运行的稳定性,大部分程序员都必须针对自己的应用来实现针对特定情况的内存分配器。当年如果不自己管理内存,很多客户端,如果计算密集频繁分配,才开始可能没什么区别,但跑个几个小时性能立马就下降下来了;服务器进程持续运行个10多天不重启,速度也会越来越慢,碎片多了嘛。如今 libc 的 malloc 也进步了很多,这样的情况比较少了,那你再做一个内存池的意义何在呢?

在你的玩具比较稳定的情况下,终于可以产生一些价值了,因为一些性能指标你无法兼得,标准的分配器往往提供了一个类似保守和中庸的做法,来针对大部分的情况,你可以做的第一步,就是打破这样的平衡,让你的分配器倾向于某些情况比如:

1. 现代计算机内存都很大,你是不是可以牺牲内存利用率为代价换取更高的内存归还/重用的效率?同时换取更快的分配速度?或许你会发现,你可以比 libc 的 malloc 平均浪费 30%内存的代价换来两倍以上的性能提升,在一些内存分配成为瓶颈的应用中起到积极的作用。

2. 比如你可以调整大小内存的比值,libc如果认为 8K以下是小内存,那么你可以不那么认为。

3. 比如如果你的系统就是一个单线程的东西,那么你是否能提供开关,完全以单线程的模式进行运作,完全绕过各种锁和针对多核进行的各种冗余操作呢?

4. 比如你的机器内存有限,你应用需要耗费大量的内存,那么你可以引入其他机制,以牺牲少量性能为代价,换取更好的内存回收效果和内存利用率。

5. 最近分配的内存在线性地址上能否尽量连续?能否在地址上尽量规避缺页中断?

6. 比如你程序里面某些对象需要被跟踪,你能否直接在分配器上实现对象跟踪机制,跟踪各种泄漏,越界问题?

7. 每个内存分配都在寻求最佳的公平,你在乎的公平是什么?


如何分配内存啊
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高...

浅谈优化SQLServer服务器内存配置
内存是影响Microsoft SQL Server系统性能的一个重要因素,SQL Server数据库安装时将为具有32MB物理内存的机器缺省配置16MB可用内存,16MB物理内存的机器缺省配置4MB可用内存。应在Microsoft SQL Server数据库安装后进行内存选项(Memory)设置,配置值为2GB。为了确定SQL Server系统最适宜的内存需求,可以从总的物...

C语言-动态分配内存 malloc & free
在一个函数中动态分配的内存,在另一个函数中操作这块内存 (1) MM是一个系统级的东西,所有的应用程序都向同一个MM申请内存。(2) 何为借出?实际上,在内存被借出时,MM只是把它管理的内存标记了一下,表示该段内存已经被占用。比如,它把每一段被占用的内存给记录下来(首地址,长度) (p0,n...

内存磁盘(RAM作为虚拟硬盘)计算机加速Primo Ramdisk设置教程(详细)_百...
首先,在Primo Ramdisk中创建一个新的虚拟硬盘,其大小取决于您的物理内存的大小(但如果使用Win7 32位系统,建议不要超过物理内存的三分之一,那么您可以直接将其设置为使用系统无法识别的内存,因为系统无法识别和使用32位以下3.25G内存,因此只需将所有计算都提供给虚拟内存即可 创建虚拟内存磁盘后,...

安卓手机的rom和ram怎么分区?
再来跟大家谈谈Android系统的RAM内存分配机制,其实在用安卓手机的时候,不用太在意剩余内存,Android 上的应用是java,当然需要虚拟机,而android上的应用是带有独立虚拟机的,也就是每开一个应用就会打开一个独立的虚拟机。如果你知道java,就能更清楚这机制了。其实和java的垃圾回收机制类似,系统有一个...

什么叫堆栈
一、预备知识―程序的内存分配 一个由c\/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)― 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap) ― 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中...

C++项目开发为什么都是这个样子?
1 项目开发都是这样的,这是为了规范,因为不同的程序员可能有不同的命名和编程习惯,所以这个很好理解,但是如果自己编可能是怎么搞怎么方便,但是一个团体做项目必须如此。2 包含string.h文件后可以用string关键字使用字符串 3 内存分配和回收的权利都赋予程序员,虽然这样很烦,什么时候都要考虑分配和...

看了一个存储器设计的电路图,有些小地方看不懂。
1)A14--A18值都=1,所以用个5与非门,输出才=0,输入端G2A(G2B)画一个圈表示低电平有效;2)A14到A18的值你必须知道,否则你就不知道何处去访问内存块,这是分配内存块地址时就必须指定的,另外M表示要访问内存块,IO就表示访问的是IO地址;3)138输出端的圈圈表示低电平输出有效;这里,地址...

电脑内存共享什么意思(显存共享内存是什么意思)
何为共享,就是显卡共享内存。比如:2G内存,显卡要512M,那么2000-512,那么等于多少,内存就得多少!这样搞,有2点好处:1.不用买独立显卡,多1G内存,才100块左右,多一个显卡要几百块 2.集成显卡,散热好于独立显卡。散热量,没那么大。缺点:就是一幕里昂然,玩3D等时,大大逊色独立显卡。至于...

求救,内存分配错误怎么办
(1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。(2)函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。(3)使用free或delete释放了内存后,没...

凌云县13157141951: 设计一个内存分配模块,帮我实现个创建,和malloc就行了哦 -
雍宰北豆: 首先给出最简单简介方案:CRT中本来就有个函数,叫 _aligned_malloc,他能实现各种内存对齐,如果在实际的项目中,使用微软给你提供的函数是最高效和安全的.面试的时候你能报出这个函数,可以看出你是一只C++老鸟.

凌云县13157141951: 如何用VHDL编写一个分配器电路,考试中急死了! -
雍宰北豆: LIBRARY IEEE; USE IEEE.Std_Logic_1164.ALL; ENTITY distributor IS PORT (D:IN Std_Logic; A:IN Std_Logic_Vector(1 DOWNTO 0); Y:OUT Std_Logic_Vector(3 DOWNTO 0)); END distributor; ARCHITECTURE behave OF distributor IS BEGIN ...

凌云县13157141951: 什么是CMA内存分配技术 -
雍宰北豆: CMA(Contiguous Memory Allocator)是智能连续内存分配技术,是Linux Kernel内存管理系统的扩展,目的在于解决视频播放(特别对于4K视频)需要预留大量连续内存导致运行内存紧张的问题.1、 简介 连续内存分配器(CMA - Contiguous ...

凌云县13157141951: 求解分配器工作原理 -
雍宰北豆: 分配器工作原理: 分配器用A::pointer A::allocate(size_type n, A<void>::const_pointer hint = 0)以进行内存分配.其中调用参数n即为需要分配的对象个数,另一调用参数hint则为可选参数,可用于在分配过程中指定新数组所在的内存地址. 但在...

凌云县13157141951: 笔记本电脑怎么设计虚拟内存?
雍宰北豆: “我的电脑”选择属性;高级;性能;设置;高级;虚拟内存;更改;选择一个空闲空间较大的非系统盘;选择“自定义大小”并按照自己的内存大小分配(推荐设置自己内存大小的1.5~3倍);点然后“设置”根据提示选择“确定”重启 华夏联盟

凌云县13157141951: linux内存管理要注意什么?
雍宰北豆: 前面我们已经分析了linux如何利用伙伴系统,slab分配器分配内存,用这些方法得到的内存在物理地址上都是连续的,然而,有些时候,每次请求内存时,系统都分配物理...

凌云县13157141951: C++之父在C++程序设计语言一书上写了一个分配器,但是我有点疑惑的就是,这个分配器适合vecto -
雍宰北豆: iterator 是一种编程模式,目的是避免成员变量的直接访问.每一个容器都有自己对应的iterator,而每个的实现都需要与其容器对应,无法共用.至于你说的断开的问题,这是事实.所以在每次使用是,都需要重新获取.举个例子,遍历vector是每次都要vector::iterator it....而不能保存一个it来访问

凌云县13157141951: c语言malloc如何实现动态分配 -
雍宰北豆: 动态的意思是和静态对比的.那我们先看一下静态是什么.当我们写一个程序的时候要定义一个放置名字的数组.中国人的名字很短最长也就是6,7个字所以在程序一开始的时候定义一个数组像这样char name[10];这样足够放置名字了.这个定义就是...

凌云县13157141951: vector<string>怎么用 -
雍宰北豆: 容器向量也是一个类模板.标准库vector类型使用须要的头文件:#include <vector>.vector 是一个类模板.不是一种数据类型,vector<int>是一种数据类型.Vector的存储空间是连续的,list不是连续存储的.定义和初始化 vector< typeName > ...

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网