En dispositivos con poca memoria RAM, como es el caso de Arduino, cuando se asigna memoria de forma dinámica puede surgir el problema de la fragmentación. Se produce cuando la memoria libre esta fragmentada en trozos tan pequeños que no se puede reservar suficiente memoria contigua libre aunque la suma total de memoria libre es mayor que la que se trata de reservar.
Veamos un ejemplo de como se produce esta fragmentación, para ello empezaremos reservando un bloque de 1000 bytes de memoria (memory1) para limitar la memoria disponible. Luego reservamos un bloque de memoria de 300 bytes (memory2) otro de un byte (memory3) y otro de 300 (memory4) de nuevo. La función de ese bloque de un único byte es evitar que al liberar los otros dos bloques estos puedan unirse en un mismo bloque. Liberamos los dos bloques de 300 bytes por lo que ahora debería haber libre un mínimo de 600 bytes libres, sin embargo si tratamos de reservar 500 bytes (memory5) y no puedo (el puntero tiene valor 0). Hay memoria libre suficiente pero esta divida en bloques más pequeños del que necesito. Con solo un byte estratégicamente colocado hemos causado un problema de fragmentación. Los bloques más pequeños (memory6) pueden reservar memoria sin problemas.
byte* memory1;
byte* memory2;
byte* memory3;
byte* memory4;
byte* memory5;
byte* memory6;
void setup() {
Serial.begin(9600);
while (!Serial) {
; // esperamos a que el puerto este inicializado
}
memory1 = (byte*) calloc (1000, sizeof(byte)); //reservamos 1000
memory2 = (byte*) calloc (300, sizeof(byte)); //reservamos 300
memory3 = (byte*) calloc (1, sizeof(byte)); //reservamos 1
memory4 = (byte*) calloc (300, sizeof(byte)); //reservamos 300
free(memory2); // liberamos 300
free(memory4); //liberamos 300
memory5 = (byte*) calloc (500, sizeof(byte)); //reservamos 500
memory6 = (byte*) calloc (300, sizeof(byte)); //reservarmos 300
Serial.println((long)memory1);
Serial.println((long)memory2);
Serial.println((long)memory3);
Serial.println((long)memory4);
Serial.println((long)memory5);
Serial.println((long)memory6);
}
void loop() {
}
Veamos un ejemplo de salida:
466 1468 1770 1774 0 1468
Se puede ver que memory5 no puede reservar memoria mientras que a memory6 se le asigna el primer bloque libre que coincide con el memory2 que acaba de ser liberado.
Este ejemplo esta pensado para una placa Arduino UNO en caso de usarlo en otra placa habrá que ajustar el tamaño del primer bloque.
Este texto mejorado y ampliado forma parte de mi libro sobre como mejorar tus programas en Arduino. Puedes echarle un vistazo aquí.
Puedes ver el ejemplo en el siguiente vídeo: