用户程序内存分配缓存简易实现

这篇具有很好参考价值的文章主要介绍了用户程序内存分配缓存简易实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在应用程序中,有时分配或首次写内存时会卡顿耗时。此文欲尝试避免该问题,想到以下方式。

  • 内存片段 —— 以指定大小内存片段为单位管理
  • 满页管理 —— 满页部分内存以多页为单位管理

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 个指定大小内存片段集中到页中进行管理,不满一页的内存片段采取上一节的管理方式。

伪码如下。

/**
 * 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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 深入理解 slab cache 内存分配全链路实现

    本文源码部分基于内核 5.4 版本讨论 在经过上篇文章 《从内核源码看 slab 内存池的创建初始化流程》 的介绍之后,我们最终得到下面这幅 slab cache 的完整架构图: 本文笔者将带大家继续从内核源码的角度继续拆解 slab cache 的实现细节,接下来笔者会基于上面这幅 slab cache 完

    2024年02月02日
    浏览(50)
  • 操作系统动态内存分配算法【C语言实现】

    题目: 采用五个算法,各自作业在1024kB空间上分配情况。 内存可变分区分配仿真算法 :首次适应,下次适应,最佳适应,最坏适应和快速分配。 使用的结构体数组表示起始地址,内存块大小,内存块状态(0空闲,1占用) void bubbleprint(struct Info info[]) 函数是为了内存块大小

    2024年02月03日
    浏览(44)
  • 深入分析arm的程序启动过程内存分配和加载区域运行区域的关系

    STM32的启动过程一 启动代码 启动代码由MCU研发商提供。 MCU一上电,首先执行的是启动代码,她是一个汇编代码。 以stm32f1为例: 首先定义堆栈,然后定义中断向量表,然后执行复位中断服务函数Reset_Handler ; Reset handler Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT Syste

    2024年02月19日
    浏览(45)
  • Jenkins工具系列 —— 插件 实现用户权限分配与管理

    点击 左侧的 Manage Jenkins — Plugins —— 左侧的 Available plugins 点击 左侧的 Manage Jenkins — Security a、安全域(委托给servlet容器、Jenkins自己的用户数据库、LDAP、Unix 用户/组数据库)- Jenkins自己的用户数据库 b、允许用户注册 c、授权策略(任何人都可以做任何事、传统模式、登入

    2024年02月10日
    浏览(44)
  • 【Servlet学习三】实现一个内存版本的简易计算器~

    目录 一、方式1:使用form表单的形式(不推荐) 🌈1、前端代码:HTML文件 🌈2、后端代码:Calculator_form.java文件 🌈3、最终效果 二、方式2:使用ajax形式(最常用重点!!!) 🌈1、前端代码:HTML文件 🌈2、后端代码:Calculator_ajax.java文件 🌈3、最终效果  注意: (1)前端

    2024年02月12日
    浏览(43)
  • 【C语言高阶篇】成为编程高手必学内容,程序中的动态内存分配我不允许还有人不会!

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 :《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,又是新的一天开始了,今天给大家带来的是动态内存规划这一章节!    ⛳️ 我们在创建变量的时候大家都知道大小是固

    2024年02月16日
    浏览(48)
  • SpringBoot使用Caffeine实现内存缓存

    一、引入依赖 caffeine提供了四种缓存策略:分别为手动加载、自动加载、异步手动加载、异步自动加载。 二、手动加载 时间: expireAfterWrite: 基于写入时间 expireAfterAccess:基于访问时间 expireAfter:可以根据读更新写入来调整有效期 权重: maximumWeight:基于权重的容量策略,主要应

    2024年02月07日
    浏览(32)
  • 【智能合约实战】——实现简易版dapp【对于用户年龄的获取和更新】

    最近我司准备引入区块链技术,打算将整车上链,学习新技术的机会千载难逢,于是乎加入智能合约研究小组,以下是我一周了解和学习的入门级小成果 Dapp就是去中心化应用程序的简称 ,是以某种方式连接到区块链的在线应用程序。从理论上讲,dapp是运行在节点服务器上面

    2023年04月08日
    浏览(34)
  • Jtti:Linux内存管理中的slab缓存怎么实现

    在Linux内存管理中,slab缓存是一种高效的内存分配机制,用于管理小型对象的内存分配。slab缓存的实现是通过SLAB分配器来完成的,它在Linux内核中对内存分配进行优化。 SLAB分配器将内存分为三个区域:slab、partial、和empty。 Slab区域: Slab区域用于保存完整的内存对象。当有

    2024年02月15日
    浏览(47)
  • 华为OD机试真题 Java 实现【简易内存池】【2023 B卷 200分 考生抽中题】

    华为OD机试 2023B卷题库疯狂收录中,刷题 点这里 请实现一个简易内存池,根据请求命令完成内存分配和释放。 内存池支持两种操作命令,REQUEST和RELEASE,其格式为: 1、REQUEST 请求的内存大小表示请求分配指定大小内存,如果分配成功,返回分配到的内存首地址;如果内存不足

    2024年02月12日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包