我一直在阅读 Linux 上的内存不足情况,手册页中的以下段落让我思考:
默认情况下,Linux 遵循乐观的内存分配策略。这意味着当 malloc() 返回非 NULL 时,不能保证内存确实可用。这是一个非常糟糕的错误。如果发现系统内存不足,一个或多个进程将被臭名昭著的 OOM 杀手杀死。[...]
考虑到 operator new 实现最终会在某个时候调用 malloc,是否有任何保证 new 会在 Linux 上实际抛出?如果没有,如何处理这种明显无法检测到的错误情况?
我一直在阅读 Linux 上的内存不足情况,手册页中的以下段落让我思考:
默认情况下,Linux 遵循乐观的内存分配策略。这意味着当 malloc() 返回非 NULL 时,不能保证内存确实可用。这是一个非常糟糕的错误。如果发现系统内存不足,一个或多个进程将被臭名昭著的 OOM 杀手杀死。[...]
考虑到 operator new 实现最终会在某个时候调用 malloc,是否有任何保证 new 会在 Linux 上实际抛出?如果没有,如何处理这种明显无法检测到的错误情况?
这取决于; 您可以使用 vm.overcommit_memory配置内核的过度使用设置。
Herb Sutter 几年前讨论过这种行为实际上是如何不符合 C++ 标准的:
“在某些操作系统上,包括特别是Linux,内存分配总是成功的。句号。即使请求的内存真的不可用,分配怎么能总是成功?原因是分配本身只是记录了对内存的请求;在幕后,(物理或虚拟)内存实际上并没有真正提交给请求进程,具有真正的后备存储,直到内存被实际使用。
“请注意,如果 new 直接使用操作系统的设施,那么 new 将始终成功,但任何后来的无辜代码(如 buf[100] = 'c'; 可能会抛出或失败或停止。从标准 C++ 的角度来看,这两种效果不符合标准,因为 C++ 标准要求如果 new 不能提交足够的内存,它必须失败(这不会),并且像 buf[100] = 'c' 这样的代码不应抛出异常或以其他方式失败(这可能)。”
你无法在你的软件中处理它,纯粹而简单。
对于您的应用程序,您将收到一个完全有效的指针。一旦您尝试访问它,它将在内核中生成页面错误,内核将尝试为它分配一个物理页面,如果它不能......繁荣。
但是正如您所看到的,所有这些都发生在内核内部,您的应用程序看不到这一点。如果它是一个关键系统,您可以在系统上一起禁用过度使用。
我认为 malloc 仍然可以返回 NULL。原因是可用系统内存(RAM + 交换)和进程地址空间中的数量之间存在差异。
例如,如果您在标准 x86 linux 上向 malloc 请求 3GB 内存,它肯定会返回 NULL,因为考虑到用户空间应用程序的内存量,这是不可能的。
如果我错了,请原谅我,但是尝试将分配的内存归零是否足以保证您拥有所需的每一个字节?或者甚至只是写入最后一个字节,如果内存不是你的,它会抛出异常吗?
如果这是真的,您可以尝试写入内存的最后一个(也是第一个?)字节,看看它是否工作正常,如果不是,您可以从 malloc 返回 null。
是的,有一个保证 new 最终会抛出。无论是否过度使用,地址空间的数量都是有限的。因此,如果您继续分配内存,迟早您会用完地址空间,并且 new 将被迫抛出。