iOS标准库中常用数据结构和算法之内存池
Posted iOS开发
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS标准库中常用数据结构和算法之内存池相关的知识,希望对你有一定的参考价值。
作者丨欧阳大哥2013
https://www.jianshu.com/p/34bd3e5c5b4e
⛲️内存池
内存池提供了内存的复用和持久的存储功能。设想一个场景,当你分配了一块大内存并且填写了内容,但是你又不是经常去访问这块内存。这样的内存利用率将不高,而且无法复用。
而如果是采用内存池则可以很轻松解决这个问题:你只需要从内存池中申请这块内存,设置完内容后当不需要用时你可以将这块内存放入内存池中,供其他地方在申请时进行复用,而当你再次需要时则只需要重新申请即可。
内存池提供了内存分配编号而且设置脏标志的概念,当你把分配的内存放入内存池并设置脏标志后,系统就会在适当的时候将这块内存的内容写回到磁盘,这样当你再次根据内存编号来访问内存时,系统就又会从磁盘中将内容读取到内存中去。
功能:在ios中提供了一套内存池管理的API,你可以用这套API来实现上述的功能,而且系统内部很多功能也是借助内存池来实现内存复用和磁盘存储的。
头文件: #include <mpool.h>
平台: BSD系统,linux系统
一、内存池的创建、同步和关闭
功能:创建和关闭一个内存池对象并和磁盘文件绑定以便进行同步操作。
函数签名:
//创建一个内存池对象
MPOOL * mpool_open(void *key, int fd, pgno_t pagesize, pgno_t maxcache);
//将内存池中的脏数据同步写回到磁盘文件中
int mpool_sync(MPOOL *mp);
//关闭和销毁内存池对象。
int mpool_close(MPOOL *mp);
参数:
key:[in] 保留字段,暂时没有用处,传递NULL即可。
fd:[in] 内存池关联的磁盘文件句柄,文件句柄需要用open函数来打开。
pagesize:[in] 内存池中每次申请和分配的内存的尺寸大小,单位是字节。
maxcache:[in] 内存池中内存页的最大缓存数量。如果池中缓存的内存数量超过了最大缓存的数量就会复用已经存在的内存,而不是每次都分配新的内存。
return:[out] 返回一个新创建的内存池对象,其他两个函数成功返回0,失败返回非0.
描述:
每一个内存池对象都会要和一个文件关联起来,以便能够实现内存数据的永久存储和内存的复用。文件句柄必须用open函数来打开,比如下面的例子:
int fd = open("/Users/apple/mpool", O_RDWR|O_APPEND|O_CREAT,0660);
当我们不需要使用某个内存页时或者内存页的内容有改动则我们需要将这个内存页放入回内存池中,并将页标志为脏(DIRTY)。这样系统就会在适当的时候将此内存页的数据写回到磁盘文件中,同时此内存页也会在后续被重复利用。
当我们想将所有设置为脏标志的内存页立即写入磁盘时则需要调用mpool_sync函数进行同步处理。
当我们不再需要内存池时,则可以通过mpool_close来关闭内存池对象,需要注意的是关闭内存池并不会将内存中的数据回写到磁盘中去。
二、内存池中内存的获取
功能:从内存池中申请分配一页新的内存或者获取现有缓存中的内存。
函数签名:
//从内存池中申请分配一页新的内存
void * mpool_new(MPOOL *mp, pgno_t *pgnoaddr);
//根据内存编号页获取对应的内存。
void * mpool_get(MPOOL *mp, pgno_t pgno, u_int flags);
参数:
mp:[in] 内存池对象。
pgnoaddr:[out] 用于mpool_new函数,用于保存新分配的内存页编号。
pngno:[in] 用于mpool_get函数,指定要获取的内存页的编号。
flags:[in] 此参数暂时无用。
描述:
无论是new还是get每次从内存池里面分配或者获取的内存页的大小都是由上述mpool_open函数中的pagesize参数指定的大小。
系统内部分配的内存是用calloc函数实现的,但是我们不需要手动调用free来对内存进行释放处理。
每个内存页都有一个唯一的页编号,而且每次分配的页编号也会一直递增下去。
mpool_new函数申请分配新的内存时,如果当前缓存中的内存页小于maxcache数量则总是分配新的内存,只有当缓存数量大于maxcache时才会从现有的缓存中寻找一页可以被重复利用的内存页,如果没有可以重复利用的页面,则会继续分配新的内存页。
三、内存池中内存的放回
功能:将分配或者申请的内存页放回到内存池中去,以便进行重复利用。
函数签名:
int mpool_put(MPOOL *mp, void *pgaddr, u_int flags);
参数:
mp: [in] 内存池对象。
flags:[in] 放回的属性,一般设置为0或者MPOOL_DIRTY。
return:[in] 函数调用成功返回0,失败返回非0
描述:
flags:属性如果指定为0时,表明放弃这次内存中的内容的修改,系统不会将内存中的内容写入到磁盘中,而只是将内存放入缓存中供其他地方重复使用。而如果设置为MPOOL_DIRTY时,则表明将这页内存中的数据设置为脏标志,除了同样将内存放入缓存中重复利用外,则会在适当的时候将内存中的数据写入到磁盘中,以便下次进行读取。
四、内存池磁盘读写通知
功能:注册回调函数,当某页内存要写回到磁盘或者要从磁盘中读取时就会调用指定的回调函数。
函数签名:
void mpool_filter(MPOOL *mp, void (*pgin)(void *, pgno_t, void *),
void (*pgout)(void *, pgno_t, void *), void *pgcookie);
参数:
mp:[in] 内存池对象.
pgin: [in]: 回调函数,当某个内存页的数据需要从磁盘读取时,会在读取完成后调用这个回调函数。
pgout:[in]: 回调函数,当某个内存页的数据要到磁盘时,会在写入完成后调用这个回调函数。
pgcookie: [in] 上述两个回调函数的附加参数。
描述:
因为内存池中的内存页会进行复用,以及会在适当的时候将内容同步到磁盘中,或者从磁盘中将内容读取到内存中,因此可以借助这个函数来监控这些磁盘文件和内存之间的读写操作。pgin和pgout函数的格式定义如下:
五、实例代码
内存池为iOS系统底层开发提供了一个非常重要的能力,我们可以好好利用内存池来对内存进行管理,以及一些需要进行持久化的数据也可以借助内存池来进行保存,通过内存池提高内存的重复利用率。
推荐↓↓↓
长
按
关
注
以上是关于iOS标准库中常用数据结构和算法之内存池的主要内容,如果未能解决你的问题,请参考以下文章 [ACM训练] 算法初级 之 数据结构 之 栈stack+队列queue (基础+进阶+POJ 2442+1442) 14.VisualVM使用详解15.VisualVM堆查看器使用的内存不足19.class文件--文件结构--魔数20.文件结构--常量池21.文件结构访问标志(2个字节)22.类加载机制概(代码片段