¿Por qué falla malloc() cuando hay suficiente memoria?

Resuelto pfc asked hace 7 años • 5 respuestas

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 numa, 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?

pfc avatar Jan 11 '17 21:01 pfc
Aceptado

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 numen size_tel cálculo de malloc, entonces debería funcionar como se esperaba.

Ctx avatar Jan 11 '2017 14:01 Ctx

Como otros han señalado, las matemáticas 56120*56120se desbordan inten la plataforma de OP. Ese es un comportamiento indefinido (UB).

malloc(size_t x)toma un size_targumento y los valores que se le pasan se calculan mejor usando al menos size_tmatemáticas. Esto se logra invirtiendo el orden de multiplicación. sizeof(float) * numcausa numque se amplíe al menos size_tantes 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*56120aú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 == 0no malloc(0) == NULLes 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();
}
chux - Reinstate Monica avatar Jan 11 '2017 15:01 chux - Reinstate Monica