].ptr; } else { return NULL; } } /* decrement reference counter */ void release(uint16_t handle){ printf(release ); if(handle < reference_count && handle >= 0){ struct mem_obj_t *object = &references[handle]; if (object->count <= 1){ printf(released ); free(object->ptr); reference_count--; } else { printf(decremented ); object->count--; } } }
如果你不考虑各个编译器的兼容性,你可以使用cleanup attribute在c语言中模仿自动析构。
(参考http://blog.csdn.net/haozhao_blog/article/details/14093155
http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.
html)
void cleanup_release(void** pmem) {
int i;
for(i = 0; i < reference_count; i++) {
if(references[i].ptr == *pmem)
release(i);
}
}
void usage() {
int16_t ref = create(64);
void *mem = retain(ref);
__attribute__((cleanup(cleanup_release), mem));
/* ... */
}
cleanup_release的另一个缺点是根据对象的地址去释放,而不是根据引用的个数。因此cleanup_release 在引用数组的查找上耗费巨大。一种补救的方法是是修改retain的接口,返回指向结构体mem_obj_t的指针。另外一种方法是用下面的宏,它创建变量去保存引用的数目,并且和cleanup attribute相关联。
/
* helper macros */
#define __COMB(X,Y) X##Y
#define COMB(X,Y) __COMB(X,Y)
#define __CLEANUP_RELEASE __attribute__((cleanup(cleanup_release)))
#define retain_auto(REF) retain(REF); int16_t __CLEANUP_RELEASE COMB(__ref,__LINE__) = REF
void cleanup_release(int16_t* phd) {
release(*phd);
}
void usage() {
int16_t ref = create(64);
void *mem = retain_auto(ref);
/* ... */
}
4 内存池
如果一个程序运行的时候会经过很多步骤,在每一个步骤开始的时候可能有内存池。任何时候程序需要分配内存的时候,其中的一个内存池就会被使用。根据分配内存的生命周期去选择内存池,并且内存池属于程序的某个阶段。在每一个阶段结束,内存池被立刻释放。这个方法在长期运行的程序十分受欢迎,例如守护进程,它可以在整体上降低内存的碎片化。下面是内存池管理的一个简单例子。
#include
#include
struct pool_t{ void *ptr; size_t size; size_t used; }; /* create memory pool*/ struct pool_t* create_pool(size_t size) { struct pool_t* pool = calloc(1, sizeof(struct pool_t)); if(pool == NULL) return NULL; if (size) { void *mem = calloc(1, size); if (mem != NULL) { pool->ptr = mem; pool->size = size; pool->used = 0; return pool; } } return NULL; } /* allocate memory from memory pool */ void* pool_alloc(struct pool_t* pool, size_t size) { if(pool == NULL) return NULL; size_t avail_size = pool->size - pool->used; if (size && size <= avail_size){ void *mem = pool->ptr + pool->used; pool->used += size; return mem; } return NULL; } /* release memory for whole pool */ void delete_pool(struct pool_t* pool) { if (pool != NULL) { free(pool->ptr); free(pool); } }
实现一个内存池,是一个比较困难的事情。或许一些存在的库可以满足你的需求。
GNU libc obstack
Samba talloc
Ravenbrook Memory Pool System
5) 数据结构
很多内存管理的问题可以归结为使用正确的数据结构去存储数据。选择哪种数据结构主要是由访问数据,保存数据的算法需求来决定的,类似于使用链式表,哈希表,树等能带来额外的增益,例如遍历数据结构和快速释放数据。尽管在标准库中没有支持数据结构,但是下面有一些有用的库。
For traditional Unix implementation of linked lists and trees see BSD's queue.h and tree.h macros both are part of libbsd.
GNU libavl
Glib Data Types
For additional list see http://adtinfo.org/index.html
6 )标记和清理垃圾收集器
另一种方法是使用自动垃圾回收机制,从而减少人工的释放内存。指针引用是内存不使用的时候就想释放,而垃圾机制相反,是由特定事件触发的,例如内存分配失败,或者分配达到某个水平线。标记和扫除算法是实现垃圾机制的一种方法。一开始,它会遍历堆空间中所有的以前分配的内存引用,标记哪些还可以达到的引用,清理哪些没有被标记的引用。
或许,在c语言中最出名的垃圾收集机制为Boehm-Demers-Weiser conservative garbage collector。垃圾机制的缺点是性能开销和导致程序有不确定的停顿。另一个问题是有malloc引起的,它不能被垃圾回收机制管理,需要人工管理。
另外无法预知的停顿在实时
系统中是不能接受的,但是很多环境上还是优点大于缺点。在性能的一方面,他们甚至宣称为高性能的。Mono project GNU Objective C runtime和Irssi IRC client都使用了Boehm GC。