Почему malloc() терпит неудачу, когда имеется достаточно памяти?
Я использую сервер с памятью 128 ГБ для выполнения некоторых вычислений. Мне нужно malloc()
массив 2D float размером 56120 * 56120. Пример кода выглядит следующим образом:
int main(int argc, char const *argv[])
{
float *ls;
int num = 56120,i,j;
ls = (float *)malloc((num * num)*sizeof(float));
if(ls == NULL){
cout << "malloc failed !!!" << endl;
while(1);
}
cout << "malloc succeeded ~~~" << endl;
return 0;
}
Код успешно компилируется, но когда я его запускаю, он говорит "malloc failed !!!"
. Как я рассчитывал, для хранения всего массива требуется всего 11 ГБ памяти. Прежде чем я начал код, я проверил сервер, и было доступно 110 ГБ свободной памяти. Почему происходит ошибка?
Я также обнаружил, что если я уменьшу num
до, скажем 40000, то malloc преуспеет.
Означает ли это, что существует ограничение на максимальную память, которая может быть выделена malloc()
?
Кроме того, если я изменяю способ выделения, прямо объявляя 2D-массив с плавающей точкой такого размера следующим образом:
int main(int argc, char const *argv[])
{
int num = 56120,i,j;
float ls[3149454400];
if(ls == NULL){
cout << "malloc failed !!!" << endl;
while(1);
}
cout << "malloc succeeded ~~~" << endl;
for(i = num - 10 ; i < num; i ++){
for( j = num - 10; j < num ; j++){
ls[i*num + j] = 1;
}
}
for(i = num - 11 ; i < num; i ++){
for( j = num - 11; j < num ; j++){
cout << ls[i*num + j] << endl;
}
}
return 0;
}
тогда я компилирую и запускаю его. Я получаю "Segmentation fault"
.
Как я могу это решить?
Ответы
Ответ 1
Проблема заключается в том, что ваш расчет
(num * num) * sizeof(float)
выполняется как 32-разрядное целочисленное вычисление со знаком и результат для num = 56120 равен
-4582051584
который затем интерпретируется для size_t с очень большим значением
18446744069127500032
У вас не так много памяти;) Вот почему malloc()
терпит неудачу.
Откажитесь от num
до size_t
при вычислении malloc, тогда он должен работать как ожидалось.
Ответ 2
Как указывали другие, 56120*56120
переполняет int
математику на платформе OP. Это поведение undefined (UB).
malloc(size_t x)
принимает аргумент size_t
, а переданные ему значения лучше всего вычислять, используя, по крайней мере, size_t
математику. Реверсируя порядок умножения, это выполняется. sizeof(float) * num
заставлять num
расширяться, по крайней мере, до size_t
перед умножением.
int num = 56120,i,j;
// ls = (float *)malloc((num * num)*sizeof(float));
ls = (float *) malloc(sizeof(float) * num * num);
Несмотря на то, что это предотвращает UB, это не предотвращает переполнение, поскольку математически sizeof(float)*56120*56120
может все еще превышать SIZE_MAX
.
Код может заранее обнаружить потенциальное переполнение.
if (num < 0 || SIZE_MAX/sizeof(float)/num < num) Handle_Error();
Не нужно выводить результат malloc()
.
Использование размера ссылочной переменной проще кодировать и поддерживать, чем размер для типа.
Когда num == 0
, malloc(0) == NULL
не обязательно является нарушением памяти.
Все вместе:
int num = 56120;
if (num < 0 || ((num > 0) && SIZE_MAX/(sizeof *ls)/num < num)) {
Handle_Error();
}
ls = malloc(sizeof *ls * num * num);
if (ls == NULL && num != 0) {
Handle_OOM();
}
Ответ 3
int num = 56120,i,j;
ls = (float *)malloc((num * num)*sizeof(float));
num
* num
есть 56120*56120
, который 3149454400
, который переполняет a signed int
, который вызывает поведение undefined.
Причина 40000 заключается в том, что 40000 * 40000 представляется в виде int.
Измените тип num
на long long
(или даже unsigned int
)
Ответ 4
Это контрастирует с тем, что написали другие, но для меня изменение переменной num to size_t из int позволяет выделить. Может быть, num * num переполняет int для malloc. Выполнение malloc с 56120 * 56120 вместо num * num должно вызвать ошибку переполнения.
Ответ 5
float ls[3149454400];
- это массив с автоматическим типом хранения, который обычно выделяется в стеке процессов. Степень процесса по умолчанию ограничена значением, которое намного меньше 12 ГБ, которое вы пытаетесь нажать там. Таким образом, ошибка сегментации, которую вы наблюдаете, вызвана переполнением стека, а не malloc
.