前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在C中,如何知道动态分配是否成功

在C中,如何知道动态分配是否成功

原创
作者头像
mariolu
发布2021-11-06 22:59:43
2.6K0
发布2021-11-06 22:59:43
举报

mallco是分配虚拟内存

C语言使用 malloc函数动态在堆上分配内存。malloc根据字节数的参数。如果无法分配内存,该函数将返回指向已分配内存的指针或 NULL 指针。

下面一个程序,分配 1 TB 的内存,然后在这个新分配的内存尝试写入:

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>

int main() {
  size_t large = 1099511627776;
  char *buffer = (char *)malloc(large);
  if (buffer == NULL) {
    printf("error!\n");
    return EXIT_FAILURE;
  }
  printf("Memory allocated\n");
  for (size_t i = 0; i < large; i += 4096) {
    buffer[i] = 0;
  }
  free(buffer);
  return EXIT_SUCCESS;
}

运行和编译此程序,可能会收到消息“error!”,在这种情况下,程序会立即终止……否则可能会看到“Memory allocated”(如果有 1 TB 的内存可分配),

在 macOS/clang 和 Linux/GCC 下,有时候会打印“Memory allocated”然后崩溃。

在C中,如何知道动态分配是否成功
在C中,如何知道动态分配是否成功

malloc 调用确实分配了内存,但它会分配“虚拟内存”。可能根本没有分配物理内存。系统只是为内存分配留出地址空间。当尝试使用内存时,就会发生物理分配。然后它可能会失败。

当询问程序使用多少内存时,对 malloc 的调用相加是错误的,因为这是虚拟内存使用量。

process

memory (virtual)

memory (real)

qemu

3.94 GB

32 MB

safari

3.7 GB

180 MB

动态内存按页(例如,4 kB)分配,通常比虚拟内存小得多。执行“malloc(x)”与占用 x 字节的物理内存不同。因此,依靠 malloc 确定分配是否成功是一个困难的问题。只有在写入和读取新分配的内存时才能发现。


设置是否开启过量内存

通过 /proc/sys/vm/overcommit_memory查看是否支持过量内存。Windows 不允许过量使用(但仍使用相同的虚拟/物理内存设计)。Linux 有 3 种过量使用模式,启发式(默认)、始终和从不。

https://www.kernel.org/doc/Documentation/vm/overcommit-accounting

如果 /proc/sys/vm/overcommit_memory 为0,则进程退出并显示“error!”;如果是 1,则该进程在一段时间后被 OOM 杀手终止(我的笔记本电脑没有 1T内存),通常将 /proc/sys/vm/overcommit_memory 设置为0。


mmap和mlock操作物理内存

如果要分配物理内存,请使用 mmap()(带选项的 malloc)分配地址空间,并使用 mlock() 将物理页连接到进程中的地址。如果没有足够的物理内存来满足您的请求,mlock() 将失败。


嵌入式为什么不执行malloc

这就是为什么某些嵌入式系统不执行 malloc 的原因。嵌入式系统(那些不允许 malloc 的系统)由于没有 MMU 通常没有虚拟内存,所以在那些你不能过度使用的系统上,因为没有页面错误机制。

原因很简单,通过静态分配所有内存,可以避免整个类的程序错误。没有内存泄漏,不需要解决“是否存在动态内存分配将失败的执行路径”的 NP 完全问题。它不仅与动态分配的内存总量有关,还与分配(和释放)的顺序有关。


程序可以分配比服务器上物理可用内存更多的内存吗

一个面试问题是“程序可以分配比服务器上物理可用内存更多的内存吗?”这是希望通过它了解面试者对操作系统和虚拟内存的了解程度。

“程序可以~~分配malloc~~使用比服务器上物理可用更多的内存(假设没有交换)?” 因为, malloc 从虚拟内存中分配,而不是从物理内存中分配。只有第一次通过读/写显式访问内存时,才会发生页面错误并开始页面分配。如果无法分配页面,则程序会以 SIGNAL 终止。这里,malloc 成功,因为从 VM 分配成功。但这并不能保证拥有所有的内存。即使在程序开始时分配了所有内容,仍然可能会耗尽内存......这是不可预测的。


Linux的OOM

程序很可能在 Linux 上被 OOM 杀死了。或者使用 mmap & mlock 来验证分配是否成功,但该进程仍然可以随时因任何原因被 OOM 杀死。

在 macOS 上也是如此。VM 压缩器(内核内和磁盘上压缩的“段”组合)有 64 个 gig 的限制;当达到这一点时,拥有超过 50% 压缩内存的进程可以被杀死。

参见 no_paging_space_action() :


存在过量使用的最大原因

Linux 和 macOS 上存在过量使用的最大原因:fork()。当进程分叉时,由于写时复制,绝大多数子进程的内存与父进程安全共享。但是严格的计算会说系统的总内存使用量翻了一番,这在大多数情况下太保守了。由于fork在 Unix 上非常普遍,因此很快就需要过度使用。否则,fork/exec 将停止在任何使用超过一半系统内存的进程中工作。

这就是 Linux 所做的。当复制COW 页面确实发生并且现在系统内存不足时,返回 ENOMEM 呢。内存写入不返回错误代码。OOM killer发送一个信号。

这就是为什么您要确保有足够的Swap分区来应对最坏的情况。使用Swap分区不是因为实际使用它,而是为了能够保证在最坏的情况发生时有足够的内存可用。在正常情况下,永远不应该真正使用Swap分区。

对于使用它们的每个进程,共享库可能会同时计入实内存和虚拟内存中,即使它们占用相同页面的只读或写时复制内存,并且内存映射文件可能会被全部计入在虚拟内存中,即使只有一小部分文件被读取,并且在 Linux 上,内存不足killer可能会在进程尝试真正访问过度分配的虚拟内存时选择杀死一个*不同的*进程,并且C 共享库可能不会*真正* 释放 free() 的内存,因为在下次尝试 malloc() 时保留它以避免访问内核会更快,并且这些东西都不是在标准中一成不变的,这一切都可能已经过时了几年......

没有Swap意味着只能使用驱动磁盘文件支持的页面。在内存争用期间,这可能会导致抖动。在“正常”操作期间,它会降低性能。仅在内存用完时才使用Swap分区,是一个非常普遍的误解。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • mallco是分配虚拟内存
  • 设置是否开启过量内存
  • mmap和mlock操作物理内存
  • 嵌入式为什么不执行malloc
  • 程序可以分配比服务器上物理可用内存更多的内存吗
  • Linux的OOM
  • 存在过量使用的最大原因
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档