realloc失败的几率是多少?

时间:2011-02-07 19:27:42

标签: c memory-management realloc

当用尽malloc之类的可用内存时,它会失败吗?还是有其他原因?

5 个答案:

答案 0 :(得分:11)

任何分配函数(mallocrealloccalloc以及POSIX,posix_memalign)可能由于以下任何原因而失败,可能还有其他原因:

  • 您已经耗尽了整个虚拟地址空间,或者至少耗尽了它的可用部分。在32位机器上,只有4GB的地址,可能还有1GB左右的内存供OS内存使用。即使您的计算机具有16GB的物理内存,单个进程也不能使用超过其地址的数据。
  • 您没有用尽您的虚拟地址空间,但是您已将其碎片化得如此糟糕,以至于没有可用的请求大小的连续地址范围。如果您成功分配6个512MB块,每隔一个块释放一次,然后尝试分配1GB块,则可能会发生这种情况(在32位计算机上)。当然,还有很多其他内存尺寸较小的例子。
  • 您的计算机的物理内存已耗尽,原因可能是您自己的程序使用了所有程序,或者计算机上运行的其他程序都使用了它。某些系统(默认配置中的Linux)将过度使用,这意味着malloc在这种情况下不会失败,但是当OS发现不存在时,操作系统会杀死一个或多个程序真的有足够的物理内存来绕过。但是在健壮的系统(包括禁用过度使用的Linux)上,如果没有物理内存,malloc将失败。

请注意,严格来说,出于任何原因,分配函数可以随时失败。最小化失败是一个实施质量问题。即使减少对象的大小,realloc也可能会失败;这可能发生在严格按大小分配分配的实现上。当然,在这种情况下,您可以继续使用旧的(较大的)对象。

答案 1 :(得分:4)

你应该认为realloc是这样的:

void *realloc(void *oldptr, size_t newsize)
{
   size_t oldsize = __extract_size_of_malloc_block(oldptr);
   void *newptr = malloc(newsize);

   if (!newptr)
     return 0;

   if (oldsize > newsize)
     oldsize = newsize;

   memcpy(newptr, oldptr, oldsize);
   free(oldptr);
   return newptr;
}

实现可以能够比这更有效地执行特定情况,但完全如图所示的实现是100%正确的。这意味着realloc(ptr, newsize)可能会在malloc(newsize)失败的任何时候失败;特别是即使你缩减了分配也会失败。

现在,在现代桌面系统上,有一个很好的例子,即不尝试从malloc失败中恢复,而是将malloc包装在一个函数(通常称为xmalloc)中,以终止如果malloc失败,立即编程;自然地,相同的论点适用于realloc。案例是:

  1. 桌面系统通常以“过度使用”模式运行,在这种情况下,内核将很乐意分发比RAM + swap支持更多的地址空间,假设程序实际上并不会全部使用它。如果程序 尝试使用所有程序,它将被强制终止。在这样的系统上,如果耗尽地址空间,malloc将只会失败,这在32位系统上不太可能,在64位系统上几乎不可能。
  2. 即使您没有处于过度使用模式,但很可能桌面系统拥有如此多的RAM和可用交换,在您导致malloc失败之前,用户将厌倦了他们的颠簸磁盘并强制终止您的程序。
  3. 测试从分配失败中恢复没有实用的方法;即使你有一个可以精确控制malloc调用失败的填充程序库(这样的填充程序最多是困难的,最坏的情况是不可能的,创建,取决于操作系统),你必须测试2 N 失败模式,其中N是程序中对malloc的调用次数。
  4. 参数1和2不适用于嵌入式或移动系统(但是!),但参数3在那里仍然有效。

    参数3仅适用于必须在每个呼叫站点检查和传播分配失败的程序。如果你很幸运能够使用C ++,因为它可以被使用(例如有例外),你可以依靠编译器为你创建错误恢复路径,因此测试负担大大减少了。现在,在任何更高级别的语言中都可以使用异常和垃圾收集器,这意味着即使您愿意也不用担心分配失败。

答案 2 :(得分:1)

我会说它主要是针对具体实施的。某些实现可能很可能失败。有些人可能会在realloc之前使程序的其他部分失败。始终保持防守并检查它是否失败。

请记住释放您尝试重新分配的指针。

ptr=realloc(ptr,10);

总是可能发生内存泄漏。

总是这样做:

void *tmp=ptr;
if(ptr=realloc(ptr,10)==NULL){
  free(tmp);
  //handle error...
}

答案 3 :(得分:0)

你有两个问题。

在大多数现代系统中mallocrealloc失败的可能性微不足道。只有在虚拟内存不足时才会出现这种情况。您的系统将无法访问内存而不是保留它。

W.r.t失败reallocmalloc几乎相等。 realloc可能另外失败的唯一原因是你给它一个不好的参数,即没有用mallocrealloc分配的内存或以前曾free的内存。 } d。

编辑:鉴于R.的评论。是的,您可以配置系统,使其在分配时失败。但首先,AFAIK,这不是默认值。它需要以这种方式配置的权限,作为应用程序程序员,这不是你可以依赖的。其次,即使您有一个以这种方式配置的系统,这只会在您的可用交换空间被占用时出错。通常,您的机器在此之前很久就会无法使用:它将在您的硬盘上进行机械计算(AKA交换)。

答案 4 :(得分:0)

来自zwolanswer

现在,在现代台式机系统上,有很强的理由不尝试从malloc故障中恢复,而是将malloc包装在一个函数中(通常称为xmalloc),该函数终止了如果malloc失败,请立即编程;
自然,同一论点也适用于realloc

您可以看到Git 2.29(2020年第4季度)适用的原理:xrealloc()可能发送已释放的非NULL指针,该指针已得到修复。

请参见commit 6479ea4Jeff King (peff)(2020年9月2日)。
(由Junio C Hamano -- gitster --commit 56b891e中合并,2020年9月3日)

xrealloc:不要重用零长度realloc()释放的指针

签名人:杰夫·金

此修补程序修复了xrealloc(ptr, 0)在某些平台(至少包括glibc)上可以双重释放并破坏堆的错误。

C99标准提到malloc(第7.20.3节):

If the size of the space requested is zero, the behavior is
implementation-defined: either a null pointer is returned, or the
behavior is as if the size were some nonzero value, except that the
returned pointer shall not be used to access an object.  

所以我们可能会返回NULL,或者可能会得到一个实际的指针(但是我们不允许查看其内容)。

为简化代码,我们的xmalloc()通过将NULL转换为单字节分配来处理它。 这样,呼叫者将获得一致的行为。这是在4e7a2eccc2中完成的(“ ?alloc:要求输入零字节时不返回NULL”,2005-12-29,Git v1.1.0-{{3} }。

我们还对xcalloc()xrealloc()进行了相同的处理。根据C99,这很好;上面的文字在一个段落中适用于所有三个段落。

但是在这种情况下,我们传递给realloc()的内存会怎样?即,如果我们这样做:

ret = realloc(ptr, 0);  

和“ ptr”为非NULL,但我们得到NULL的返回:“ ptr”仍然有效吗?
C99并未专门针对这种情况,但表示(见第7.20.3.4节):

The realloc function deallocates the old object pointed to by ptr and
returns a pointer to a new object that has the size specified by size.  

因此,“ ptr”现在已解除分配,我们只能查看“ ret”。
而且由于“ retNULL,这意味着我们根本没有分配的对象。但这还不是全部。它还说:

If memory for the new object cannot be allocated, the old object is
not deallocated and its value is unchanged.
[...]
The realloc function returns a pointer to the new object (which may
have the same value as a pointer to the old object), or a null pointer
if the new object could not be allocated.  

因此,如果我们看到NULL返回的大小非零,则可以预期原始对象仍然仍然有效。
但是,如果大小不为零,则模棱两可。 NULL返回可能意味着失败(在这种情况下对象是有效的),或者可能意味着我们没有成功分配任何东西,并使用NULL来表示。

glibc的{​​{1}}联机帮助页明确指出:

realloc()

同样,此StackOverflow回答“ merge”:
声称C89提供了类似的指导(但我没有副本可以验证它。)

评论What does malloc(0) return?声称Microsoft的CRT行为相同。

但是我们当前的“重试1个字节”代码再次传递了原始指针。
因此,在[...]if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr). 上,我们有效地glibc指针,然后再次尝试free(),这是未定义的行为。

这里最简单的解决方法是将“ realloc()”(我们知道是ret)传递给后续的NULL
但这意味着释放原始指针的系统将泄漏它。尚不清楚是否存在这样的系统,并且对标准的解释似乎不太可能(在这种情况下,我希望系统不会释放以简单地返回原始指针)。
但是从安全的角度来看,这很容易犯错,并且根本不会向realloc()传递零大小。