【发布时间】:2014-02-05 23:44:12
【问题描述】:
阅读Martin Sustrick's blog 中关于防止 C++ 与 C 中的“未定义行为”相关的挑战,特别是 malloc() 由于内存耗尽而失败的问题,我想起了很多很多次我感到沮丧知道在这种情况下该怎么做。
对于虚拟系统,这种情况很少见,但在嵌入式平台上,或者在与虚拟系统相关的性能下降等同于失败的情况下,就像 Martin 的 ZeroMQ 案例一样,我决定找到一个可行的解决方案,并且确实做到了。
我想问一下 StackOverflow 的读者,他们是否尝试过这种方法,以及他们的体验如何。
解决方案是在程序开始时调用 malloc() 从堆中分配一块备用内存,然后在发生内存耗尽时使用该空闲内存池来避免内存耗尽。这个想法是为了防止投降,有利于有序撤退(我昨晚正在阅读Kesselring's defense of Italy 的帐户),其中错误消息和 IP 套接字等将工作足够长的时间(希望)至少告诉用户发生了什么。
#define SPARE_MEM_SIZE (1<<20) // reserve a megabyte
static void *gSpareMem;
// ------------------------------------------------------------------------------------------------
void *tenacious_malloc(int requested_allocation_size) {
static int remaining_spare_size = 0; // SPARE_MEM_SIZE;
char err_msg[512];
void *rtn = NULL;
// attempt to re-establish the full size of spare memory, if it needs it
if (SPARE_MEM_SIZE != remaining_spare_size) {
if(NULL != (gSpareMem = realloc(gSpareMem, SPARE_MEM_SIZE))) {
remaining_spare_size = SPARE_MEM_SIZE;
// "touch" the memory so O/S will allocate physical memory
meset(gSpareMem, 0, SPARE_MEM_SIZE);
printf("\nSize of spare memory pool restored successfully in %s:%s at line %i :)\n",
__FILE__, __FUNCTION__, __LINE__);
} else {
printf("\nUnable to restore size of spare memory buffer.\n");
}
}
// attempt a plain, old vanilla malloc() and test for failure
if(NULL != (rtn = malloc(requested_allocation_size))) {
return rtn;
} else {
sprintf(err_msg, "\nInitial call to malloc() failed in %s:%s at line %i",
__FILE__, __FUNCTION__, __LINE__);
if(remaining_spare_size < requested_allocation_size) {
// not enough spare storage to satisfy the request, so no point in trying
printf("%s\nRequested allocaton larger than remaining pool. :(\n\t --- ABORTING --- \n", err_msg);
return NULL;
} else {
// take the needed storage from spare memory
printf("%s\nRetrying memory allocation....\n", err_msg);
remaining_spare_size -= requested_allocation_size;
if(NULL != (gSpareMem = realloc(gSpareMem, remaining_spare_size))) {
// return malloc(requested_allocation_size);
if(NULL != (rtn = malloc(requested_allocation_size))) {
printf("Allocation from spare pool succeeded in %s:%s at line %i :)\n",
__FILE__, __FUNCTION__, __LINE__);
return rtn;
} else {
remaining_spare_size += requested_allocation_size;
sprintf(err_msg, "\nRetry of malloc() after realloc() of spare memory pool "
"failed in %s:%s at line %i :(\n", __FILE__, __FUNCTION__, __LINE__);
return NULL;
}
} else {
printf("\nRetry failed.\nUnable to allocate requested memory from spare pool. :(\n");
return NULL;
}
}
}
}
// ------------------------------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[]) {
int *IntVec = NULL;
double *DblVec = NULL;
char *pString = NULL;
char String[] = "Every good boy does fine!";
IntVec = (int *) tenacious_malloc(100 * sizeof(int));
DblVec = (double *) tenacious_malloc(100 * sizeof(double));
pString = (char *)tenacious_malloc(100 * sizeof(String));
strcpy(pString, String);
printf("\n%s", pString);
printf("\nHit Enter to end program.");
getchar();
return 0;
}
【问题讨论】:
-
如果你使用
malloc,为什么这个标签是C++?在 C++ 中,您可以拥有不同的新处理程序 -
在不使用堆的情况下编写整个程序。许多安全关键系统采用这种想法。只是需要不同的心态
-
你可以使用内存池。
-
@user2548100 我们做到了,Ed Heal 提出的建议(重新设计组件以在 RAM 有限的小型 MC 上使用)在工作中,我只能告诉你的是:付出努力是值得的,而且效果很好。在我们的系统中实际“分配”的唯一内存是可用于特定线程(RTOS 任务)的堆栈。剩下的就是为特定应用程序获得必要的堆栈大小。
-
鉴于所有其他 cmets 涵盖了这么多好东西,我所要提供的只是为您的函数命名
tenacious_malloc()的杰出选择+1跨度>
标签: c memory memory-management malloc