22

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

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 我的所有数据?

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

4

8 回答 8

28

标准技术是引入一个新变量来保存 realloc 的返回。然后,只有在成功时才覆盖输入变量:

tmp = realloc(orig, newsize);
if (tmp == NULL)
{
    // could not realloc, but orig still valid
}
else
{
    orig = tmp;
}
于 2009-12-31T18:36:37.667 回答
9

这是一个热门话题,因为该主题基本上有两种思想流派

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

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

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

于 2009-12-31T18:30:21.843 回答
9

失败时的处理策略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×f 2 ≤ n + n×f。求解这个方程,我们得到f≤ (sqrt(5)+1)/2 = 1.618黄金比例)。1.5我大多数时候都使用一个因素。

于 2009-12-31T20:08:53.690 回答
3

使用时应遵循的第一条规则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 */;
于 2009-12-31T18:42:10.650 回答
2

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

  • 你要求那段记忆是有原因的。如果它不可用,您的程序的工作是否注定要完成,还是可以继续做事?如果是前者,你想用错误信息终止你的程序;否则,您可以以某种方式显示错误消息并继续。

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

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

于 2009-12-31T18:32:01.890 回答
2
  1. 了解应用程序框架如何处理 OOM。许多人根本不会处理OOM。大多数情况下,一个框架在没有空闲 RAM 的情况下无法正常运行,除非它在某个地方非常清楚明确地表明它会。如果框架不能处理 OOM 并且是多线程的(现在很多都是),那么在很多情况下,OOM 将成为该过程的结束。即使它不是多线程的,它仍然可能接近崩溃。您是否退出流程或框架可能是一个有争议的问题;可预测的立即退出可能比在不久的将来某个半随机点的崩溃好一点。

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

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

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

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

当然,严峻的考验是您是否通过高正常运行时间以及罕见的数据损坏、丢失或崩溃来充分满足用户的需求。在某些情况下,如果应用程序崩溃时具有监控进程以重新启动它是有用的。

关于重新分配:

检查 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)中将“大多数情况”更改为“很多情况”。

我知道您说过如果无法分配内存,则假设“可以做某事”。但是内存管理是一个非常全局的考虑(!)。

于 2009-12-31T19:29:45.223 回答
1

还有另一个可能来自 realloc 的细微错误。来自返回的 NULL 指针的内存泄漏是众所周知的(但很少会偶然发现)。我的程序中偶尔会发生一次来自 realloc 调用的崩溃。我有一个动态结构,它使用类似于这个的 realloc 自动调整其大小:

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

我犯的错误是没有检查 m->max == 0,这释放了内存区域。并由我的 m->data 指针制成一个陈旧的指针。

我知道这有点离题,但这是我使用 realloc 时遇到的唯一真正问题。

于 2009-12-31T18:51:25.050 回答
1

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

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

于 2016-02-26T04:41:23.367 回答