在应用程序中,有时分配或首次写内存时会卡顿耗时。此文欲尝试避免该问题,想到以下方式。
- 内存片段 —— 以指定大小内存片段为单位管理
- 满页管理 —— 满页部分内存以多页为单位管理
1 内存片段
以指定大小内存片段实现内存分配缓存较简单,即直接组织、分配、释放指定大小的内存片段即可。
伪码如下。文章来源地址https://www.toymoban.com/news/detail-584137.html
/**
* memca.h
* 内存片段内存分配 缓存接口声明及
* 公共类型、代码实现
*/
#ifndef MEMCA_H
#define MEMCA_H
#include <inttypes.h>
#include <pthread.h>
#include <stdlib.h>
#define ma_max(a, b) ((a) > (b) ? (a) : (b))
#define ma_lock(ma) pthread_mutex_lock(&(ma)->lock)
#define ma_unlock(ma) pthread_mutex_unlock(&(ma)->lock)
#define ma_lock_init(m) ({\
(m) = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;\
})
struct memca {
const char *name;
uint16_t size;
pthread_mutex_t lock;
uint32_t max;
uint32_t num;
void *head;
};
void mca_init (struct memca *ma);
void mca_close(struct memca *ma);
void * mca_alloc(struct memca *ma);
void mca_free (struct memca *ma, void *v);
static inline
void * ma_alloc(struct memca *m, void **h, uint32_t dec)
{
void *v;
if ((v=*h)) {
*h = *(void **)v;
m->num -= dec;
}
return v;
}
static inline
void ma_free(struct memca *m, void **h, void *v, int nochk)
{
if (nochk || m->num < m->max) {
*(void **)v = *h;
m->num += !nochk;
*h = v;
v = NULL;
}
free(v);
return ;
}
#endif
/**
* memca.c
* 内存片段分配 缓存管理实现
*/
#include "memca.h"
#include <stdlib.h>
void mca_init(struct memca *ma)
{
uint16_t size = ma_max(ma->size, sizeof(void *));
uint32_t max = ma->max;
void *last = NULL;
ma_lock_init(ma->lock);
ma->head = NULL;
ma->size = size;
ma->num = 0;
while (max--) {
void *v = malloc(size);
if (!v)
return ;
if (!last) {
ma->head = last = v;
} else {
*(void **) last = v;
last = v;
}
*(void **)v = NULL;
ma->num += 1;
}
return ;
}
void mca_close(struct memca *ma)
{
void *v;
while ((v=ma->head)) {
ma->head = *(void **)v;
ma->num -= 1;
free(v);
}
return ;
}
void * mca_alloc(struct memca *ma)
{
void *v;
ma_lock(ma);
v = ma_alloc(ma, &ma->head, 1);
ma_unlock(ma);
return v ?: malloc(ma->size);
}
void mca_free(struct memca *ma, void *v)
{
ma_lock(ma);
ma_free(ma, &ma->head, v, 0);
ma_unlock(ma);
return ;
}
2 满页管理
将满足 n n n 页的 x x x 个指定大小内存片段集中到页中进行管理,不满一页的内存片段采取上一节的管理方式。文章来源:https://www.toymoban.com/news/detail-584137.html
伪码如下。
/**
* pageca.h
* 声明内存片段连续管理类型及接口
*/
#ifndef PAGECA_H
#define PAGECA_H
#include "memca.h"
struct pageca {
struct memca ma;
void *page;
void *start, *end;
};
void pca_init (struct pageca *pa);
void pca_close(struct pageca *pa);
void * pca_alloc(struct pageca *pa);
void pca_free (struct pageca *pa, void *v);
#endif
/**
* pageca.c
* 连续内存片段管理实现
*/
#include <unistd.h>
#include <string.h>
#include "memca.h"
#include "pageca.h"
static unsigned pagesize;
#define pagesize() ({ \
pagesize ? \
pagesize : \
(pagesize=getpagesize()); \
})
#define pa_to_ma(pa) ((struct memca*)(pa))
#define pa_real_size(ma) ({ma_max(ma->size, sizeof(void*));})
void pca_init(struct pageca *pa)
{
struct memca *ma = pa_to_ma(pa);
uint16_t size = pa_real_size(ma );
uint64_t pgnr = size * ma->max / pagesize();
void *s, *e;
ma->max -= pgnr * pagesize() / size;;
mca_init(ma);
if (!pngr) reurn ;
s = malloc(pgnr * pagesize());
pa->page = pa->start = s;
e = pa->end = (char*)s + pgnr*pagesize();
if (!s)
return ;
for (/* none */; s < e; s += size) {
if (s + (size<<1) > pa->end)
break;
*(void**) s = s + size;
}
*(void**) s = NULL;
return ;
}
void pca_close(struct pageca *pa)
{
mca_close(pa_to_ma(pa));
free(pa->start);
pa->page = \
pa->start = pa->end = NULL;
return ;
}
void * pca_alloc(struct pageca *pa)
{
void *v;
struct memca *ma = pa_to_ma(pa);
ma_lock(ma);
v = ma_alloc(ma, &pa->page, 0);
ma_unlock(ma);
return v ?: mca_alloc(ma);
}
void pca_free(struct pageca *pa, void *v)
{
struct memca *ma = pa_to_ma(pa);
if (pa->start <= v && v < pa->end) {
ma_lock(ma);
ma_free(ma, &pa->page, v, 1);
ma_unlock(ma);
return ;
}
mca_free(ma, v);
return ;
}
void * pca_swap(struct pageca *pa, void *v)
{
void *vp;
struct memca *ma = pa_to_ma(pa);
if (pa->start <= v && v < pa->end)
return v;
vp = ma_alloc(ma, &pa->page, 0);
if (!vp)
return v;
memcpy(vp, v, ma->size);
ma_free(ma, &ma->head, v, 1);
return vp;
}
3 优劣分析
- 上述两种方法皆不需目标内存片段额外提供内存用于其管理,由此内部会限制最小内存片段为指针大小
- 以指定大小内存片段为单位缓存内存分配时,在诸如大并发做初始化操作即memca_init 时,memca 内部管理的内存片段可能会位于其他模块的物理页上,若 memca 内的内存片段一直处于缓存状态,则会导致未释放内存片段对应物理页不会被释放;可以在小并发阶段执行 mca_init 操作或者将 memca.max 的值设置得比欲用内存片段数小一些,由此在超缓存分配后调用释放即 mca_free 时刺激一些内存片段的真正释放,也可以尝试用满页管理方式代替代码片段策略
- 当同时出现满页及零散内存片段管理时,若某阶段分配了超过缓存数 max 的内存,下一某阶段所释放内存都是整页管理范围内的内存,则会导致超过缓存数的内存片段不能释放,可以在内存片段释放即 pca_free 后通过尝试检查通过 pca_alloc 分配结点是否在满页范围内,不在该范围则尝试调用 pca_swap 来让满页内存中的内存片段代替,然后释放掉该零散内存,这样就可以释放掉超过缓存数的内存,不会让内存一直处于上升态的风险中
到了这里,关于用户程序内存分配缓存简易实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!