`
totoxian
  • 浏览: 1030713 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

一个基于链表的内存管理方案

 
阅读更多

在OpenVPN中,一种很不错的内存管理方案是基于链表的,该方案的实现使用了一个gc_arena结构体,该结构体的作用就是将所有的动态分配的内存块收集汇集起来,然后就可以在一个地方统一释放,c语言对动态内存管理的劣势就是无法跟踪动态内存,因此也就将管理动态内存的事情交给了程序员来完成,然后就有了无数次的由于内存泄露导致的bug,虽然c++实现了很多内存管理方案,比如智能指针之类的,但是其复杂性无不揉进了c++这种重量级语言本身,使得本已复杂的语言带上特定的库之后更加臃肿,并且在c++中实现的动态内存管理无非就是使用了构造函数,析构函数之类的,规范相当地复杂,如果有人觉得不复杂,那么他要么是一个初学c++的人,要么是一个学了c但是没有学好的人。
如果将这个话题展开谈,动态内存管理其实并不是很复杂,其唯一的技术难题就是跟踪动态内存的分配,而不是具体的释放时机,一旦所有的动态分配被成功跟踪了,那么释放时机就是程序员的事情了,以前程序员由于跟踪不了动态内存分配而导致内存泄露,那实在不是程序员的错,毕竟程序越来越大越不可控,而每个人也不一定都是高手,但是如果你时刻都能得到当前分配了哪些内存,但是还是泄露,那就是你个人问题了,所以说,只要做到动态内存分配的汇总即可。OpenVPN的内存管理方案就是巧妙地将动态分配的内存组织成了链表,然后在“合适”的时机来统一释放,这个释放不是释放某一个内存块,而是释放被收集的所有的内存块。首先看一下这个机制的基础设施:
struct gc_entry { //gc机制的单向链表
struct gc_entry *next;
};
struct gc_arena {//最重要的外层结构体,gc机制构建于之上
struct gc_entry *list;
};
static inline struct gc_arena gc_new (void)
{ //分配一个gc,注意是inline,否则返回栈上的缓冲区是错误的
struct gc_arena ret;
ret.list = NULL; //初始化
return ret;
}
static inline void gc_free (struct gc_arena *a)
{ //链表的释放,释放掉链表上所有的动态内存区域
if (a->list)
x_gc_free (a);
}
void x_gc_free (struct gc_arena *a)
{ //具体的释放动作
struct gc_entry *e;
e = a->list;
a->list = NULL;
while (e != NULL) { //遍历链表的元素,逐个释放之
struct gc_entry *next = e->next;
free (e);
e = next;
}
}
void *gc_malloc (size_t size, bool clear, struct gc_arena *a)
{ //重要的分配函数
void *ret;
if (a) {
struct gc_entry *e; //注意下面分配的空间大小要加上gc_entry的大小,gc_entry负责管理
e = (struct gc_entry *) malloc (size + sizeof (struct gc_entry));
check_malloc_return (e);
ret = (char *) e + sizeof (struct gc_entry); //只把除去管理数据的内存返回给调用者
e->next = a->list; //将该块内存链接入链表
a->list = e;
}
...
if (clear) //分配一块清0内存
memset (ret, 0, size);
return ret;
}
以上代码很清晰,下面则是使用上述机制的一个例子:
int mmalloc(struct gc_arena *gc)
{//如果下面的换成malloc(1000)的话,则瞬间就完蛋了。
char *buf = (char *)gc_malloc (1000, 1, gc);
}
int main(int argc, char **argv)
{
struct gc_arena gc = gc_new();
gc.list = NULL;
int i = 0, j = 0;
while (1) {
i++;
while (j++ < 10) {
mmalloc(&gc);
}
j = 0;
gc_free(&gc);
}

}
何谓“合适”的时机,到底在什么时候释放呢?这实际上是个问题,答案就是需要你自己掌握,也就是说你必须知道什么时候那些分配的缓冲区没有用了,这个gc机制所做的仅仅是帮你搜集到所有的缓冲区,而不是负责帮你决定释放的时机,如果你理解linux kernel的计数器机制的话,那么一定会为这种内存管理机制叹为观止,不过说实话,即使是计数器机制也是需要程序员自己把握什么时候调用free,而在free中递减计数器,计数器机制为你做的只是控制计数器为当前正在使用该内存的路径数量,具体何时该释放还是需要程序员自己控制,实际上不要贪图任何毫无代价的自动内存管理机制,也没有那样的机制,目前实现的需要付出代价的内存管理机制主要有两种,一种是c++牺牲了简单性原则实现的利用诸如构造函数/析构函数之类的机制,它实质上是利用一些c++的固定标准来实现的,另一种则是牺牲了性能和可控性的垃圾收集机制,java使用该种方式,java虚拟机需要维护一个对象引用图,定期触发垃圾收集而将游离的图节点内存回收。可见唯一不需要付出系统代价的就是c的实现了,可是却需要程序员在写代码时付出代价,不管怎样,就简单就是美的原则来讲我宁可使用C的方法也不喜欢c++或者java那种看似简单实则臃肿的方式

分享到:
评论

相关推荐

    hashdic:快速,小型(200 LOC),CC ++,基于哈希表的键值字典,自动增长,MurMur,基于链表的存储桶

    ##快速,小型(200 LOC),C / C ++,基于哈希表的键/值字典,自动增长,MurMur,基于链表的存储桶,零依赖关系。 对于我的库(JavaScript类似于C ++中的便捷var ),我需要一个字典解决方案来查找对象内部的键。 ...

    基于socket聊天程序编写实验报告

    1. 硬件平台:多台PC机的一个局域网、Windows XP/2000、AMD Athlon64 X2 4000+ 、内存256MB以上、硬盘80G以上。 2. 软件平台:delphi7 1.3 运行环境 本系统基于WIN NT 和ACCESS XP设计,适用于WIN2000/WIN XP...

    基于C++、文件操作和Huffman算法实现图片压缩源码+使用说明+详细注释+sln解决方案.zip

    压缩文件时,定义一个内存缓冲区: &gt; char \*pBuffer; //其大小视原文件压缩后的大小 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如...

    C++数据抽象和问题求解(第6版).[美]Frank M. Carrano(带详细书签).pdf

    4.5 比较基于数组的实现和基于链表的实现 148 第5章 作为问题求解技术的递归 155 5.1 定义语言 156 5.1.1 语法知识基础 156 5.1.2 两种简单的语言 158 5.2 代数表达式 160 5.2.1 代数表达式的类型 160 5.2.2 ...

    基于C++实现的HTTP服务器改进版源码+项目使用说明+详细注释.zip

    **①基于升序链表的定时器**:将每个需要监控的连接注册为一个时间结点,每个结点包括双向指针以及期待的时间和回调函数指针;包含添加、删除以及调整结点;回调函数主要实现对当前连接的close;\ **②基于信号和...

    基于PCAP的网络入侵检测系统C语言实现源码+详细项目说明+作业报告(课程大作业).zip

    1.项目代码功能经验证ok,确保稳定可靠运行。欢迎下载使用!...对于IPv4地址, 较优的方案是创建一个大小为$\frac{2^{32}}{2^{5}} = 2^{27}$的`uint32_t`数组, 记录IP地址是否出现. 更多详情见项目说明!!!

    IOS缓存管理之YYCache使用详解

    前言: 最近一直在致力于为公司app添加缓存功能,为了寻找一个最佳方案,这几天先做个技术预研,经过这两天的查找资料基本上确定了两个开源框架进行...整个实现基于_YYLinkedMap,它是一个双向链表,除了存储了字典_dic外

    循环数据结构在稳定、安全、Rust 中的概念实现的证明。

    在链表中,拼接另一个链表,或者拆分链表的一部分,都是 O(1) 操作,但将 N 个元素从一个Vec转移到另一个Vec至少是一个 O(N) 操作。基于 Arena 的解决方案将允许以预期的算法复杂性实现所有操作,但这是以永远无法从...

    《计算机操作系统》期末复习指导

    2、功能的观点 操作系统是一个计算机资源管理系统,它负责计算机系统的全部资源的分配、控制、调度和回收。 3、用户的观点 操作系统是计算机与用户之间的接口,用户通过这种接口使用计算机。 4、软件的观点 操作系统...

    传智播客扫地僧视频讲义源码

    15_信息系统框架集成第三方产品案例_框架实现第一个socket类厂商实现 16_信息系统框架集成第三方产品案例_第二个socket类厂商实现 17_信息系统框架集成第三方产品案例_加解密抽象类和加解密厂商类实现 18_信息系统...

    java 面试题 总结

    然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中...

    sesvc.exe 阿萨德

    如果第一个不匹配,则判断它的下一个是红黑树还是链表。 红黑树就按照树的查找方式返回值。 不然就按照链表的方式遍历匹配返回值。 从这两个核心方法(get/put)可以看出 1.8 中对大链表做了优化,修改为红黑树之后...

    fault-tolerant-stack:能够容忍任意数量的丢失链接的堆栈的实现

    (想象一个单向链表:被内存故障损坏的单个指针会使列表中的所有以下元素都无法访问。)内存故障很容易观察到(尽管它们通常会导致系统崩溃),并且最近的各种工作都描述了现代 DRAM 对此类故障的敏感性,甚至可以...

    超级有影响力霸气的Java面试题大全文档

     SessionBean: Stateless Session Bean 的生命周期是由容器决定的,当客户机发出请求要建立一个Bean的实例时,EJB容器不一定要创建一个新的Bean的实例供客户机调用,而是随便找一个现有的实例提供给客户机。...

    数据结构(C++)有关练习题

    设计一个构造函数,当对象结束时,要释放整个二叉搜索树所占的内存空间(提示,通过后序遍历算法找到叶结点,并删除叶结点,不断重复此过程,直到整科树为空); 2、实现1所要求的代码后,运行设计好的代码,将...

    atomic_queue:C ++无锁队列

    循环缓冲区以固定缓冲区大小为代价,回避了基于链表的队列中固有的内存回收问题。 有关更多详细信息,请参见。 固定的缓冲区大小可能没有太大的限制,因为一旦队列变得大于最大预期大小,这表明存在元素处理速度...

    C++ MFC实现飞机大战游戏

    CImageList是一个“图象列表”是相同大小图象的集合,每个图象都可由其基于零的索引来参考。可以用来存放爆炸效果的一张图片,使用Draw()函数来绘制在某拖拉操作中正被拖动的图象,即可连续绘制出多张图片做成的爆炸...

    【RT-Thread作品秀】基于ART-PI的数字图像处理与识别-电路方案

    提取的灰度图后,我们还需要再进行一次二值化处理,因此设定一个阈值,当灰色像素大于这个阈值我们将它改为255,低于这个值变成0。这样就得到一帧只有0和225值的图像。 得到二值化图像后,我们便可以寻找要识别物体...

    基于Motion-JPEG2000的远程图像传输技术 (2012年)

    对于远程无线视频图像传输,由于数据量大、通讯带宽窄等原因常常导致传输速度慢,难以满足实际工程需要为解决这一问题,提出了基于Mofion-JPEG2000序列图像压缩技术的远程无线高速图像传输系统设计方案,在C/S模式...

Global site tag (gtag.js) - Google Analytics