当它用完类似的可用内存malloc
或可能有其他原因时它会失败吗?
5 回答
任何分配函数(malloc
、realloc
、calloc
和 POSIX 上的posix_memalign
)都可能由于以下任何原因失败,也可能由于其他原因:
- 您已经用完了整个虚拟地址空间,或者至少是其中的可用部分。在 32 位机器上,只有 4GB 的地址,并且可能保留 1GB 左右供操作系统内核使用。即使你的机器有 16GB 的物理内存,单个进程也不能使用超过它的地址。
- 你没有用完你的虚拟地址空间,但是你已经把它碎片化得很厉害,以至于没有可用的连续地址范围的请求大小。如果您成功分配 6 512MB 块,每隔一个释放一个块,然后尝试分配 1GB 块,则可能会发生这种情况(在 32 位机器上)。当然,还有很多其他内存较小的例子。
- 您的机器已用完物理内存,这可能是因为您自己的程序已全部使用,或者机器上运行的其他程序已全部使用。某些系统(默认配置中的 Linux)会过度使用,这意味着
malloc
在这种情况下不会失败,但是当操作系统发现没有足够的物理内存可以运行时,它会在以后杀死一个或多个程序。但是在健壮的系统上(包括禁用过度使用的 Linux),malloc
如果没有剩余的物理内存,将会失败。
请注意,严格来说,分配功能可以在任何时候因任何原因失败。最小化失败是一个实施质量问题。realloc
即使减小对象的大小,也有可能失败;这可能发生在按大小严格隔离分配的实现上。当然,在这种情况下,您可以简单地继续使用旧的(更大的)对象。
You should think of realloc
as working this way:
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;
}
An implementation may be able to do specific cases more efficiently than that, but an implementation that works exactly as shown is 100% correct. That means realloc(ptr, newsize)
can fail anytime malloc(newsize)
would have failed; in particular it can fail even if you are shrinking the allocation.
Now, on modern desktop systems there is a strong case for not trying to recover from malloc
failures, but instead wrapping malloc
in a function (usually called xmalloc
) that terminates the program immediately if malloc
fails; naturally the same argument applies to realloc
. The case is:
- Desktop systems often run in "overcommit" mode where the kernel will happily hand out more address space than can be backed by RAM+swap, assuming that the program isn't actually going to use all of it. If the program does try to use all of it, it will get forcibly terminated. On such systems, malloc will only fail if you exhaust the address space, which is unlikely on 32-bit systems and nigh-impossible on 64-bit systems.
- Even if you're not in overcommit mode, the odds are that a desktop system has so much RAM and swap available that, long before you cause
malloc
to fail, the user will get fed up with their thrashing disk and forcibly terminate your program. - There is no practical way to test recovery from an allocation failure; even if you had a shim library that could control exactly which calls to
malloc
failed (such shims are at best difficult, at worst impossible, to create, depending on the OS) you would have to test order of 2N failure patterns, where N is the number of calls to malloc in your program.
Arguments 1 and 2 do not apply to embedded or mobile systems (yet!) but argument 3 is still valid there.
Argument 3 only applies to programs where allocation failures must be checked and propagated at every call site. If you are so lucky as to be using C++ as it is intended to be used (i.e. with exceptions) you can rely on the compiler to create the error recovery paths for you, so the testing burden is much reduced. And in any higher level language worth using nowadays you have both exceptions and a garbage collector, which means you couldn't worry about allocation failures even if you wanted to.
我会说它主要是特定于实现的。一些实现很可能会失败。在 realloc 之前,有些程序的其他部分可能会失败。始终保持防御并检查它是否失败。
并记住释放您尝试重新分配的旧指针。
ptr=realloc(ptr,10);
总是可能的内存泄漏。
总是这样做:
void *tmp=ptr;
if(ptr=realloc(ptr,10)==NULL){
free(tmp);
//handle error...
}
你有两个问题。
在大多数现代系统上,失败malloc
或失败的可能性可以忽略不计。realloc
这仅在您用完虚拟内存时发生。您的系统将无法访问内存而不是保留它。
Wrt失败realloc
并且malloc
几乎相等。另外可能失败的唯一原因realloc
是您给了它一个错误的参数,即尚未分配的内存malloc
or realloc
or 以前曾被free
d.
编辑:鉴于 R. 的评论。是的,您可以配置您的系统,使其在分配时失败。但首先,AFAIK,这不是默认设置。它需要以这种方式配置权限,作为应用程序程序员,这不是您可以指望的。其次,即使您有一个以这种方式配置的系统,这也只会在您的可用交换空间被耗尽时出错。通常,您的机器在此之前很久就无法使用:它将在您的硬盘上进行机械计算(AKA 交换)。
现在,在现代桌面系统上,有充分的理由不尝试从
malloc
故障中恢复,而是包装malloc
在一个函数(通常称为xmalloc
)中,该函数在失败时立即终止程序malloc
;
自然,同样的论点也适用于realloc
。
您可以看到 Git 2.29(2020 年第四季度)应用了该原则:可以xrealloc()
发送已被释放的非 NULL 指针,该指针已被修复。
请参阅Jeff King ( ) 的提交 6479ea4(2020 年 9 月 2 日)。(由Junio C Hamano 合并 -- --在提交 56b891e中,2020 年 9 月 3 日)peff
gitster
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()
处理返回。 这样来电者就会得到一致的行为。这是在4e7a2eccc2中完成的(“ :当要求零字节时不返回”,2005-12-29,Git v1.1.0 -- merge)。NULL
?alloc
NULL
我们也给予
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
”。
并且由于 "ret
" isNULL
,这意味着我们根本没有分配对象。但这还不是全部。它还说: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
联机帮助页realloc()
明确表示:[...]if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr).
同样,这个 StackOverflow 对“返回什么
malloc(0)
? ”的回答:
声称 C89 给出了类似的指导(但我没有副本来验证它)。对此答案的评论“有什么意义
malloc(0)
?” 声称微软的 CRT 行为相同。但是我们当前的“用 1 字节重试”代码再次传递了原始指针。
依此类推glibc
,我们有效地free()
指向指针,然后realloc()
再次尝试指向它,这是未定义的行为。这里最简单的解决方法是将 "
ret
" (我们知道是这样NULL
)传递给后续realloc()
。
但这意味着不释放原始指针的系统会泄漏它。目前尚不清楚是否存在任何此类系统,并且该标准的解释似乎不太可能(我希望一个不会解除分配的系统在这种情况下简单地返回原始指针)。
但是为了安全起见很容易犯错,而且根本不传递零大小realloc()
。