如何在内存失败时处理realloc?

问题说明了这一切,但这里是一个例子:

typedef struct mutable_t{ int count, max; void **data; } mutable_t; void pushMutable(mutable_t *m, void *object) { if(m->count == m->max){ m->max *= 2; m->data = realloc(m->data, m->max * sizeof(void*)); } // how to handle oom?? m->data[m->count++] = object; } 

如何处理内存耗尽而不是所有数据的NULL?

编辑 – 让我们假设有一些事情可以做,例如在某处释放一些内存或者至少告诉用户“你不能那样做 – 你的内存不足”。 理想情况下,我想留下那里分配的东西。

标准技术是引入一个新变量来保存realloc的返回值。 然后,如果成功,则只覆盖输入变量:

 tmp = realloc(orig, newsize); if (tmp == NULL) { // could not realloc, but orig still valid } else { orig = tmp; } 

关于realloc()失败时该怎么做的策略取决于你的应用程序。 这个问题过于笼统,无法回答所有可能的情况。

其他一些说明:

永远不会做:

 a = realloc(a, size); 

如果realloc()失败,则会丢失原始指针,并且realloc()不会free()原始内存,因此会出现内存泄漏。 相反,做:

 tmp = realloc(a, size); if (tmp) a = tmp; else /* handle error */ 

我要做的第二点是次要的,可能不是那么重要,但无论如何都要了解它是很好的:增加由f因子分配的内存是好的。 假设你首先使用malloc() n个字节。 然后你需要更多的内存,所以realloc()的大小为n×f 。 那么你需要更多的内存,所以你需要n×f 2个字节。 如果希望realloc()使用前两个内存块中的空间,则需要确保n×f2≤n+ n×f 。 求解该等式,得到f≤(sqrt(5)+1)/ 2 = 1.618 ( 黄金比率 )。 我大多数时候使用1.5倍。

这是一个热点按钮主题,因为基本上有两个关于这个主题的思想流派

  1. 检测OOM,并使该函数返回错误代码。
  2. 检测OOM并尽快崩溃您的进程

我个人在营地#2。 对于非常特殊类型的应用程序,OOM是致命期。 确实,完美编写的代码可以处理OOM,但很少有人理解如何编写在没有内存的情况下安全的代码。 实际上做的更少,因为它几乎不值得付出努力。

我不喜欢将错误代码传递给OOM的调用函数,因为它相当于告诉调用者“我失败了,你无能为力”。 相反,我更喜欢快速崩溃,因此产生的转储尽可能具有指导性。

在使用realloc时,您应该遵循的第一条规则是不将realloc的返回值分配给传递给它的同一指针。 这个

 m->data = realloc(m->data, m->max * sizeof(void*)); 

不好。 如果realloc失败,则返回空指针,但不释放旧内存。 上面的代码将使m->data空,而以前由m->data指向的旧内存块很可能会成为内存泄漏(如果没有其他引用)。

realloc的返回值应首先存储在单独的指针中

 void **new_data; ... new_data = realloc(m->data, m->max * sizeof(void*)); 

然后,您可以检查成功/失败,并在成功的情况下更改m->data的值

 if (new_data != NULL) m->data = new_data; else /* whatever */; 

这完全是你的问题! 以下是一些标准:

  • 你问这个记忆是有原因的。 如果它不可用,你的程序工作是否注定失败还是可以继续做? 如果是前者,您希望使用错误消息终止程序; 否则,您可以以某种方式显示错误消息并继续。

  • 是否有可能换空间? 您可以使用使用较少内存的算法回复您尝试的任何操作吗? 这听起来像很多工作,但实际上可能会继续你的程序操作,尽管最初没有足够的内存。

  • 如果没有这些数据且没有足够的内存,你的程序是否会继续跛行是不是错了? 如果是这样,您应该以错误消息终止。 杀死你的程序比盲目地继续处理不正确的数据要好得多。

  1. 了解应用程序框架如何处理OOM。 许多人根本不会处理OOM。 大多数情况下,框架在无自由RAM条件下无法正常运行,除非它在某个地方非常明确且明确地说明了它。 如果框架不能处理OOM并且是multithreading的(现在很多都是如此),那么在很多情况下,OOM将成为该过程的结束。 即使它不是multithreading的,它仍然可能接近崩溃。 退出流程还是框架确实可能是一个有争议的问题; 一个可预测的立即退出可能比在不久的将来某个半随机点崩溃要好一些。

  2. 如果你使用一个单独的专用子内存池(即不是你通常的malloc)来定义一组操作,这些操作只受OOM内存使用的限制(即当前操作被回滚或干净地中止) OOM用于子内存池,而不是整个进程或主内存池),并且该子池也不是由应用程序框架使用,或者如果您的框架和应用程序其余部分的WHOLE旨在维护有意义的状态和在无自由RAM条件下继续运行(在内核模式和某些类型的系统编程中很少见但并非闻所未闻)您可能正确地返回错误代码而不是使进程崩溃。

  3. 理想情况下,处理过程中的大部分内存分配(甚至更理想的是所有分配)应在处理过程中尽快分配,理想情况是在正确开始之前,以最大限度地减少数据完整性丢失和/或数量的问题。如果失败则需要回滚编码。 在实践中,很多时候,为了节省编程成本和项目时间,保护数据完整性应用程序依赖于数据库事务,并要求用户/支持人员检测GUI崩溃(或服务器崩溃)并在外出时重新启动应用程序发生内存错误,而不是以最佳方式编写以应对和回滚任何和所有数千个潜在OOM情况。 然后,工作重点是尝试限制应用程序暴露于超载情况,这可能包括额外的validation和数据大小限制以及同时连接和查询。

  4. 即使您检查报告的内存量是多少,通常其他代码可能会像您一样分配或释放内存,更改内存检查的基础并可能导致OOM。 因此,在分配之前检查可用的可用RAM通常不是确保应用程序在可用RAM限制内运行并保持数据完整性以满足用户满足的问题的可靠解决方案。

  5. 最好的情况是知道应用程序在所有可能的情况下需要多少内存,包括任何框架开销,并将该数字保持在应用程序可用的RAM量内,但系统通常很复杂,外部依赖性指示数据大小因此实现这一点可能是不现实的。

当然,酸测试是你通过高正常运行时间和不经常的数据损坏,丢失或崩溃来充分满足用户。 在某些情况下,具有监视进程的应用程序在崩溃时重新启动它是有用的。

关于realloc:

检查realloc的返回值 – 将其放入临时变量中。 如果请求的新大小> 0,则只关注它是否为NULL。 在其他情况下,将其放在非临时变量中:

例如

  void* temp = realloc(m->data, m->max * sizeof(void*)); if (m->max!=0&&temp==NULL) { /* crash or return error */ } m->data =(void**)temp; 

编辑

在(1)中将“大多数情况”改为“很多情况”。

我认识到,如果无法分配内存,你会假设“可以做某事”。 但内存管理是一个非常全球化的考虑因素(!)。

还有另一个微妙的错误可以来自realloc。 来自返回的NULL指针的内存泄漏是众所周知的(但很难偶然发现)。 我在我的程序中偶然发生了一次来自realloc调用的崩溃。 我有一个动态结构,通过类似这一个的realloc自动调整其大小:

 m->data = realloc(m->data, m->max * sizeof(void*)); 

我犯的错误是没有检查m-> max == 0,它释放了内存区域。 并且从我的m->数据指针制作一个陈旧的。

我知道这有点偏离主题,但这是我对realloc唯一真正的问题。

我遇到了这个问题。配置是OS:win7(64); IDE:vs2013; Debug(Win32)。
当我的realloc由于内存而返回null时。我有两个解决方案:

1.更改项目的属性,以启用大地址。
2.将我的解决方案平台从Win32更改为x64。