【问题标题】:Calloced memory appears to be NULLCalloced 内存似乎为 NULL
【发布时间】:2017-08-03 06:49:18
【问题描述】:

有哪些可能的情况可以让下面的代码执行下面sn-p中的if条件?就我而言,我无法说明if 语句执行的任何原因。

#include <stdio.h>
#include <stdlib.h>
void main(void){
int Nod = 1024 * 8; //Nod contains the number of nodes
double *MM; //MM is a square matrix it can contain very large number of data 10^10
MM = calloc(8 * Nod * 8 * Nod, sizeof(double));
if (MM == NULL)exit(0);
//then MM will then be passed to some other functions say
eigenvalue(MM);}

我正在使用在一个非常大的程序中间进行此检查的 FEM 代码。有趣的事实是,当我运行代码时,它显示出异常行为。有时程序就在这里停止。有时它工作得很好。值得一提的是,当程序以粗网格运行时,即当Nod 需要计算的节点数量较少时,程序运行良好。但是当使用精细的网格时,不幸的是程序崩溃了。该程序在具有 128GB 内存的迷你工作站中运行。该程序占用 1GB(左右)的 RAM。

【问题讨论】:

  • 该特定程序总是以零状态返回,无论采用何种分支。当执行到达其右括号并返回时,主函数(并且只有主函数)具有隐式return 0;
  • 8 * Nod * 8 * Nod 是 2³² 所以整数溢出。使用比int 更大的类型。
  • 阅读手册页了解 calloc 和可能的返回值。
  • 如果您使用的是稀疏矩阵,您可以只存储在地图或无序地图上填充的单元格。无需分配大量内存。
  • 大号给谁? Nod? Nod 仅表示要计算多少节点,这完全在 int 数据类型的范围内。

标签: c dynamic-allocation calloc finite-element-analysis


【解决方案1】:

两个明显的问题:

  1. 计算8 * Nod * 8 * Nod 将是int 类型,它可能不够大(在您的平台上)来容纳结果。你可能想要size_t Nod。如果值不是常量,您可能需要检查溢出(可能使用特定于平台的函数,例如 GCC 的 __builtin_mul_overflow())。
  2. 您使用calloc() 的结果而不检查它是否不是NULL。如果分配器找不到足够大的连续块,它将失败,您应该在继续之前进行测试。

永远不要忽略使用它来报告错误的库函数的返回值。

【讨论】:

  • 如果只是数据类型溢出,它不会每次都失败吗?
  • @louigi600:这取决于。有符号溢出是undefined,所以你不能依赖任何东西。在“最佳”情况下(即最坏情况),您最终会得到一个值,当转换为 size_t 时,它对于数据来说已经足够大了。如果幸运的话,您会立即遇到错误或无法满足的值,从而使您的程序崩溃并鼓励进行一些调试。
  • @Tony S. 好的,但即便如此,他如何在虚拟内存不足的 4Gb 系统上分配巨大的 32Gb 块来做到这一点?我检查了一台 32 位机器(如果 malloc 失败,则使用 exit(1)),它以 0 退出......这到底是怎么回事?我很困惑!
  • 在少 PAE 的 32 位机器上,虚拟内存地址空间不能大于 4Gb ......分配假设的 32Gb ......还有其他问题!
  • @luigi,这在很大程度上取决于您的平台。例如,大多数 Linux 安装都会过度提交,因此分配似乎成功,但如果/当您尝试访问所有分配时会出错。如果这很重要,则值得设置适当的可调参数(我忘记了如何执行此操作;我认为有一个 sysctl 可以设置全局值,但也可能有一个每个进程的设置)。
【解决方案2】:

来自手册页:

   The malloc() and calloc() functions return a pointer to the allocated  memory  that
   is  suitably  aligned  for  any kind of variable.  On error, these functions return
   NULL.  NULL may also be returned by a successful call to malloc() with  a  size  of
   zero, or by a successful call to calloc() with nmemb or size equal to zero.e here

现在,在您的情况下,分配零大小的内存不是露水,因此返回 NULL 的唯一其他原因是分配内存失败。 在 sn-p 中,您显示您正在分配 4294967296 个元素(1024 * 1024 * 64 * 64),其大小为 32Gb ram 的双精度(8 字节)。 现在您的系统肯定有这么多内存,但在任何给定时间,它可能不会将所有内存都放在一个连续的可分配块中,因此 calloc 可能因此而失败。

另一件需要注意的是内存过度使用主要由

/proc/sys/vm/overcommit_memory
or vis sysctl  vm.overcommit_memory

默认情况下,overcommit_memory 设置为 0,但最安全的组合可能是将其设置为 2。有关此内容的更多详细信息,请参阅 proc 手册页或内核文​​档/vm/overcommit-accounting。

vm.overcommit_ratio
vm.overcommit_kbytes
vm.nr_overcommit_hugepages

还有其他 sysctl 设置,用于控制您的系统是否/如何处理内存过度使用。

即便如此,我已尽我所能不允许在 32 位 linux 机器上过度使用内存,但我仍然能够让巨大的 32Gb callot 不返回 null(我认为这本身很奇怪,因为一台少 PAE 的 32 位机器只能寻址总共 4Gb 的虚拟内存,即使它有 PAE,它一次也只允许寻址 4Gb)。

【讨论】:

  • 顺便提一下,我现在正在我只有 4GB RAM 的 PC 上测试这个程序。就在我发布问题时,该程序在所述行中被视为失败。但有趣的是,该程序现在正在运行......!并且有很多密集的网格......!我只是想要一个提示......是什么导致了这种异常行为。
  • 基本上,我写的建议与 Toby Speight 提出的第二点大致相同……只是多写了几个字和数字来说明我是如何来到 32Gb 的。令我感到奇怪的是,在 4Gb ram 机器上,您可以分配 32Gb 对象……即使 long 的大小是一个字节(而且绝对不是),它仍然会分配 4Gb ……在 e 4Gb带有实时操作系统的机器我很难相信您可以分配 4Gb 。也许是虚拟内存:你在 4Gb 机器上有多少交换空间?
  • @AhmedAfifKhan 这种行为绝不是“异常”-您实际上是在尝试分配大量内存,这些内存可能在某些时候可用或可能不可用-而且您甚至不检查对于分配后直接的返回值,你的程序会失败。
  • 分页 = 1280MB。它正在运行……!好吧,我也很困惑为什么它在我的机器上运行......!我在这里只提到了一个变量,只是为了说明。声明了几个巨大的double 大小的数组...!
  • 只是出于好奇:您在为 AI 分析大数据方面的工作是什么?如果您关闭 4Gb 机器上的交换,它仍然有效吗?
猜你喜欢
  • 2021-10-01
  • 1970-01-01
  • 2023-03-05
  • 2015-09-17
  • 2021-09-14
  • 1970-01-01
  • 2018-11-08
  • 1970-01-01
相关资源
最近更新 更多