Encuentra fugas de memoria en C++

Saad Aslam 16 febrero 2024
  1. Pérdidas de memoria en C++
  2. Manejar pérdidas de memoria en C++
  3. Formas de evitar fugas de memoria en C++
  4. Use Valgrind para encontrar fugas de memoria en C++
  5. Utilice la biblioteca CRT para encontrar fugas de memoria en C++
Encuentra fugas de memoria en C++

Este artículo explicará las fugas de memoria, sus causas, cómo identificarlas y cómo prevenirlas usando el lenguaje de programación C++.

Pérdidas de memoria en C++

Se dice que la memoria está “filtrada” si la parte de la memoria que el programador asignó previamente a un propósito se utiliza para otro. Cuando sucede algo así, el programador no desasigna correctamente el recurso.

En este punto, el programa no necesita el uso de dicha memoria RAM. En consecuencia, no hay razón para mantener la reserva de ese asiento.

La fuga de memoria ocurre en C++ cuando los programadores asignan memoria utilizando la palabra clave nuevo pero no desasignan la memoria utilizando la función eliminar() o el operador eliminar[]; esto da como resultado que se pierda la memoria. La fuga de memoria es causada por el operador de “eliminar” incorrecto la mayor parte del tiempo.

El operador delete[] puede liberar datos en una matriz.

Si cambiamos el aceite de nuestro automóvil pero nos olvidamos de reemplazar un sello o apretar un tornillo simultáneamente, es probable que nos encontremos con problemas importantes al conducir.

El motor de nuestro automóvil eventualmente se atascará ya que todo el aceite finalmente saldrá del automóvil; La programación en C++ conducirá precisamente al mismo resultado.

Cuando un programador asigna memoria dinámicamente pero luego no la libera, se produce una fuga de memoria y la asignación de memoria se puede realizar de la siguiente manera.

int *data;
data = (int *)malloc(8);
char *login = new char(40);

Cuando asignamos memoria dinámicamente, esa memoria proviene del montón, que es una parte de la memoria del sistema que utiliza C++. La pila es de donde se toman las variables y funciones.

Habrá una pérdida de memoria si no agregamos el siguiente código para borrar estas asignaciones. Estas fugas se acumularían con el tiempo y, dependiendo de la lógica de nuestro programa, pueden hacer que nuestra aplicación falle.

free(data);
delete login;

Aquí hay otro caso en el que se crea una función para asignar ocho bytes de espacio de almacenamiento dinámico para un puntero. Esto ocupará 8 bytes en una computadora de 64 bits.

Esos bytes no se liberarán después de que el programa complete la ejecución.

#include <iostream>
using namespace std;

void data_leak() { double *pointer = new double(28.54); }
int main() { data_leak(); }

Agregar el siguiente código de bucle en el bloque de código anterior hará que se asignen un millón de bytes, pero nunca se liberarán. Probablemente deberíamos guardar los archivos abiertos antes de ejecutar este código.

Incluso los sistemas operativos más avanzados no tendrían problemas con esto, pero si tuviéramos que agregar alguna instrucción adicional al bucle for, podríamos tener problemas. Esta es la razón por la cual las fugas de memoria pueden ser tan peligrosas.

for (int j = 0; j < 150000; j++) {
  data_leak();
}

Manejar pérdidas de memoria en C++

Primero, creamos una función llamada func_to_handle_memory_leak(),, luego declaramos un puntero de tipo entero para manejar una pérdida de memoria, luego asignamos un valor entero con la palabra clave new int().

void func_to_handle_memory_leak() { int* ptr = new int(6); }

Ahora usamos la función delete() para borrar la memoria anterior y evitar pérdidas de memoria en el programa. Todas las actividades se llevarán a cabo tan pronto como se llame a la función y la memoria asignada se liberará antes de que la función regrese.

delete (ptr);

En la función main, llamamos a nuestra función func_to_handle_memory_leak().

int main() {
  func_to_handle_memory_leak();
  return 0;
}

Código fuente completo:

#include <iostream>
using namespace std;

void func_to_handle_memory_leak() {
  int* ptr = new int(6);
  delete (ptr);
}
int main() {
  func_to_handle_memory_leak();
  return 0;
}

Formas de evitar fugas de memoria en C++

  1. En lugar de administrar manualmente la memoria, debe hacer un esfuerzo para utilizar punteros inteligentes siempre que sea posible.
  2. En lugar de char\*, debe usar std::string. La clase std::string es ultrarrápida y altamente optimizada; se encarga de toda la gestión de la memoria internamente.
  3. La mejor manera de evitar fugas de memoria en C++ es tener algunas llamadas nuevas y eliminar a nivel de programa. Todo lo que requiera memoria dinámica debe enterrarse dentro de un objeto RAII que libera la memoria cuando se sale del alcance; RAII asigna memoria en el constructor y la libera en el destructor para garantizar que la memoria se desasigne cuando la variable abandone el ámbito actual.
  4. La memoria debe asignarse utilizando la palabra clave nuevo, y la memoria debe desasignarse utilizando la palabra clave eliminar, con todo el código escrito entre estas dos instrucciones.

Use Valgrind para encontrar fugas de memoria en C++

Una fuga de memoria es uno de los tipos de error de programación más insidiosos, ya que no se manifiesta como un problema notable hasta que haya agotado la memoria de su sistema y falle una llamada a malloc.

De hecho, sin recolección de elementos no utilizados, aproximadamente la mitad de su esfuerzo puede dedicarse a garantizar que la memoria se libere correctamente cuando se trata de lenguajes como C o C++.

Para ver la lista actual de herramientas compatibles, simplemente ejecute Valgrind, luego elija la que desea usar al ejecutar su código.

Ejecutar Valgrind con la herramienta memcheck nos permitirá verificar el consumo de memoria preciso; proporcionará un resumen de cada llamada gratuita y malloc.

Para explicarlo, utilizaremos un programa básico llamado memoryleakdemo.

#include <stdlib.h>

int main() {
  char *x = new char[100];
  return 0;
}

Esto mostrará información sobre el programa, incluida una lista de llamadas malloc sin las llamadas gratuitas correspondientes.

% valgrind --tool=memcheck --leak-check=yes memoryleakdemo

Sabemos que una llamada a malloc en main fue responsable de la fuga de memoria, pero no podemos ubicar el número de línea específico. El problema es que no construimos el programa usando la opción -g de GCC, que proporciona símbolos de depuración.

El siguiente resultado es lo que obtenemos si recompilamos el programa usando símbolos de depuración.

==2022== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2022==    at 0x1B900DD0: malloc (vg_replace_malloc.c:131)
==2022==    by 0x804840F: main (memoryleakdemo.c:5)

Utilice la biblioteca CRT para encontrar fugas de memoria en C++

Las fugas de memoria se pueden localizar e identificar mediante el depurador de Visual Studio y las bibliotecas C Run-Time (CRT).

Activar la detección de fugas de memoria

Debe incluir las siguientes declaraciones en su aplicación para activar las funciones del montón de depuración.

#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#include <stdlib.h>

Después de haber usado estos comandos para activar las funciones de almacenamiento dinámico de depuración, puede insertar una llamada a _CrtDumpMemoryLeaks antes de un punto de salida de la aplicación para que se muestre un informe de pérdida de memoria cuando finalice su programa.

_CrtDumpMemoryLeaks();

El informe de pérdida de memoria se envía a la pestaña Depurar de la ventana Salida cuando se ejecuta _CrtDumpMemoryLeaks. Este es el comportamiento predeterminado.

Puede usar la función _CrtSetReportMode para enviar el informe a un lugar diferente.

La salida de la fuga de memoria de la biblioteca CRT:

El _CrtDumpMemoryLeaks generará un informe de pérdida de memoria similar al que se muestra a continuación si su aplicación no declara _CRTDBG MAP ALLOC.

Detected memory leaks!
Dumping objects ->
{18} normal block at 0x00780E80, 64 bytes long.
 Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

El informe de pérdida de memoria se verá así si su aplicación especifica _CRTDBG MAP ALLOC.

Detected memory leaks!
Dumping objects ->
c:\users\username\documents\projects\leaktest\leaktest.cpp(20) : {18}
 normal block at 0x00780E80, 64 bytes long.
 Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
Autor: Saad Aslam
Saad Aslam avatar Saad Aslam avatar

I'm a Flutter application developer with 1 year of professional experience in the field. I've created applications for both, android and iOS using AWS and Firebase, as the backend. I've written articles relating to the theoretical and problem-solving aspects of C, C++, and C#. I'm currently enrolled in an undergraduate program for Information Technology.

LinkedIn

Artículo relacionado - C++ Memory