¿Por qué falla malloc() cuando hay suficiente memoria?
Estoy usando un servidor con 128 GB de memoria para hacer algunos cálculos. Necesito malloc()
una matriz flotante 2D de tamaño 56120 * 56120. Un código de ejemplo es el siguiente:
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;
}
El código se compila correctamente pero cuando lo ejecuto, dice "malloc failed !!!"
. Como calculé, sólo se necesitan unos 11 GB de memoria para contener toda la matriz. Antes de comenzar con el código, revisé el servidor y había 110 GB de memoria libre disponibles. ¿Por qué ocurre el error?
También descubrí que si reduzco num
a, digamos, 40000, entonces el malloc tendrá éxito.
¿Significa esto que hay un límite en la memoria máxima que puede asignar malloc()
?
Además, si cambio la forma de asignación, declaro directamente una matriz flotante 2D de tal tamaño, de la siguiente manera:
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;
}
luego lo compilo y lo ejecuto. Yo tengo un "Segmentation fault"
.
¿Como puedo resolver esto?
El problema es que tu cálculo
(num * num) * sizeof(float)
se realiza como un cálculo de entero con signo de 32 bits y el resultado para num=56120 es
-4582051584
Que luego se interpreta para size_t con un valor muy grande
18446744069127500032
No tienes tanta memoria ;) Ésta es la razón por la que malloc()
falla.
Transmitido num
en size_t
el cálculo de malloc, entonces debería funcionar como se esperaba.
Como otros han señalado, las matemáticas 56120*56120
se desbordan int
en la plataforma de OP. Ese es un comportamiento indefinido (UB).
malloc(size_t x)
toma un size_t
argumento y los valores que se le pasan se calculan mejor usando al menos size_t
matemáticas. Esto se logra invirtiendo el orden de multiplicación. sizeof(float) * num
causa num
que se amplíe al menos size_t
antes de la multiplicación.
int num = 56120,i,j;
// ls = (float *)malloc((num * num)*sizeof(float));
ls = (float *) malloc(sizeof(float) * num * num);
Aunque esto evita UB, no evita el desbordamiento, ya que matemáticamente sizeof(float)*56120*56120
aún puede exceder SIZE_MAX
.
El código podría detectar un posible desbordamiento de antemano.
if (num < 0 || SIZE_MAX/sizeof(float)/num < num) Handle_Error();
No es necesario emitir el resultado de malloc()
.
Usar el tamaño de la variable referenciada es más fácil de codificar y mantener que ajustar el tamaño al tipo.
Cuando num == 0
no malloc(0) == NULL
es necesariamente falta de memoria.
Todos juntos:
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();
}