diferencia - De administración de memoria, corrupción de montón y C++
stack memory (12)
Además, realmente solucioné el problema std :: string. ¿Cómo? Sustituyéndolo por un vector, compilando, luego reemplazando la cadena de nuevo. Fue chocando constantemente allí, y eso se solucionó a pesar de que ... no podía. Hay algo desagradable allí, y no estoy seguro de qué.
Eso suena como si realmente sacudieras un pollo. Si no sabe por qué está funcionando ahora, entonces todavía está roto, y casi seguro le morderá nuevamente más tarde (después de que haya agregado aún más complejidad).
Entonces, necesito ayuda. Estoy trabajando en un proyecto en C ++. Sin embargo, creo que de alguna manera he logrado corromper mi montón. Esto se basa en el hecho de que agregué una std::string
a una clase y le asigné un valor de otra std::string
:
std::string hello = "Hello, world./n";
/* exampleString = "Hello, world./n" would work fine. */
exampleString = hello;
se bloquea en mi sistema con un volcado de pila. Así que, básicamente, necesito detenerme y revisar todo el código y las cosas relacionadas con la administración de la memoria y descubrir dónde me equivoqué. La base de código todavía es pequeña (alrededor de 1000 líneas), por lo que es fácilmente factible.
Aún así, estoy sobre mi cabeza con este tipo de cosas, así que pensé en tirarlo allí. Estoy en un sistema Linux y he valgrind
con valgrind
, y aunque no sé completamente lo que estoy haciendo, me informó que el destructor de std::string
no era válido. Debo admitir que recibí el término ''Heap Corruption'' de una búsqueda en Google; también se apreciarían artículos de propósito general sobre este tipo de cosas.
(En antes de rm -rf ProjectDir
, hacer nuevamente en C #: D)
EDITAR: No he dejado en claro, pero lo que estoy pidiendo son algunas maneras de un consejo de diagnóstico de este tipo de problemas de memoria. Sé que las cosas de std :: string son correctas, así que es algo que he hecho (o un error, pero no es un problema con Select). Estoy seguro de que podría verificar el código que he escrito y ustedes muy inteligentes verían el problema en un instante, pero quiero agregar este tipo de análisis de código a mi ''caja de herramientas'', por así decirlo.
Ah, si quieres saber cómo depurar el problema, es simple. Primero, consigue un pollo muerto. Entonces, comienza a sacudirlo .
En serio, no he encontrado una forma consistente de rastrear este tipo de errores. Debido a que hay tantos problemas potenciales, no hay una lista de verificación simple para llevar a cabo. Sin embargo, recomendaría lo siguiente:
- Ponte cómodo en un depurador.
- Comienza a buscar en el depurador para ver si puedes encontrar algo sospechoso. Compruebe especialmente para ver qué está sucediendo durante
exampleString = hello;
línea. - Verifique para asegurarse de que se está bloqueando en el
exampleString = hello;
línea, y no al salir de algún bloque envolvente (lo que podría provocar que los destructores disparen). - Verifique cualquier magia de puntero que pueda estar haciendo. Aritmética del puntero, lanzamiento, etc.
- Verifique todas sus asignaciones y desasignaciones para asegurarse de que coincidan (no hay desasignaciones dobles).
- Asegúrese de no devolver ninguna referencia o puntero a los objetos en la pila.
También hay muchas otras cosas que probar. Estoy seguro de que otras personas también compartirán ideas.
Algunos lugares para comenzar:
Si está en Windows y utiliza C ++ 6 visuales (espero que nadie lo siga usando), su implementación de std :: string no es segura para los hilos, y puede llevar a este tipo de cosas.
En mi lugar de trabajo anterior utilizamos Compuware Boundschecker para ayudar con esto. Es comercial y muy caro, por lo que puede no ser una opción.
Aquí hay un par de bibliotecas gratuitas que pueden ser de alguna utilidad
http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/
http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx
Espero que ayude. ¡La corrupción de la memoria es un lugar sucky en el que estar!
Ejecuta Purify.
Es una herramienta casi mágica que informará cuando estés jugando a la memoria que no deberías tocar, la pérdida de memoria al no liberar cosas, la doble liberación, etc.
Funciona a nivel de código de máquina, por lo que ni siquiera tiene que tener el código fuente.
Una de las conferencias telefónicas de proveedores más agradables que tuve fue cuando Purify encontró una fuga de memoria en su código, y pudimos preguntar, "¿es posible que no esté liberando memoria en su función foo ()" y escuche el asombro en sus voces.
Pensaron que estábamos depurando a los dioses, pero luego les contamos el secreto para que pudieran ejecutar Purify antes de que tuviéramos que usar su código. :-)
http://www-306.ibm.com/software/awdtools/purify/unix/
(Es bastante caro, pero tienen una descarga de evaluación gratuita)
El código era simplemente un ejemplo de dónde mi programa estaba fallando (se asignó en la pila, Jim). Realmente no estoy buscando ''qué he hecho mal'', sino ''cómo diagnostico lo que he hecho mal''. Enseña a un hombre a pescar y todo eso. Aunque estoy viendo la pregunta, no lo he dejado suficientemente claro. Gracias a Dios por la función de edición. : '')
Además, realmente solucioné el problema std :: string. ¿Cómo? Sustituyéndolo por un vector, compilando, luego reemplazando la cadena nuevamente. Fue chocando constantemente allí, y eso se solucionó a pesar de que ... no podía. Hay algo desagradable allí, y no estoy seguro de qué. Sin embargo, quería verificar la única vez que asigné memoria manualmente en el montón:
this->map = new Area*[largestY + 1];
for (int i = 0; i < largestY + 1; i++) {
this->map[i] = new Area[largestX + 1];
}
y borrarlo:
for (int i = 0; i < largestY + 1; i++) {
delete [] this->map[i];
}
delete [] this->map;
No he asignado una matriz 2d con C ++ antes. Parece funcionar.
Podría ser corrupción de montón, pero es muy probable que sea corrupción de la pila. Jim tiene razón. Realmente necesitamos un poco más de contexto. Esas dos líneas de fuente no nos dicen mucho en forma aislada. Podría haber varias cosas que causen esto (que es la verdadera alegría de C / C ++).
Si te sientes cómodo publicando tu código, incluso podrías lanzarlo todo en un servidor y publicar un enlace. Estoy seguro de que obtendrá muchos más consejos de esa manera (algunos de ellos, sin duda, sin relación con su pregunta).
Por lo que puedo decir, tu código es correcto. Suponiendo que exampleString es una std :: string que tiene un alcance de clase como el que describes, deberías ser capaz de inicializarlo / asignarlo de esa manera. Tal vez hay algún otro problema? Tal vez un fragmento de código real lo ayude a contextualizarlo.
Pregunta: ¿exampleString es un puntero a un objeto string creado con new?
Una de las técnicas de depuración que uso con frecuencia (excepto en los casos de la rareza más extrema) es dividir y conquistar. Si su programa falla actualmente con algún error específico, divídalo por la mitad de alguna manera y vea si todavía tiene el mismo error. ¡Obviamente, el truco es decidir dónde dividir tu programa!
Su ejemplo como dado no muestra suficiente contexto para determinar dónde podría estar el error. Si alguien más intentara tu ejemplo, funcionaría bien. Por lo tanto, en su programa, intente eliminar la mayor cantidad de material extra que no nos mostró y vea si funciona en ese momento. Si es así, agregue el otro código de a poco a la vez hasta que comience a fallar. Entonces, lo que acaba de agregar es probablemente el problema.
Tenga en cuenta que si su programa es multiproceso, entonces probablemente tenga problemas más grandes. Si no, entonces deberías poder reducirlo de esta manera. ¡Buena suerte!
Además de herramientas como Boundschecker o Purify, su mejor opción para resolver problemas como este es simplemente obtener una buena lectura del código y familiarizarse con el código en el que está trabajando.
La corrupción de la memoria es una de las cosas más difíciles de solucionar y, por lo general, estos tipos de problemas se resuelven gastando horas / días en un depurador y observando algo así como "¡oye, el puntero X se usa después de que se eliminó!".
Si ayuda a alguno, es algo que mejorará a medida que adquiere experiencia.
Su asignación de memoria para la matriz parece correcta, pero asegúrese de verificar todos los lugares donde también accede a la matriz.
Una vez tuvimos un error que eludió todas las técnicas habituales, valgrind, purificar, etc. La falla solo ocurrió en máquinas con mucha memoria y solo en grandes conjuntos de datos de entrada.
Eventualmente lo rastreamos utilizando puntos de vigilancia de depuración. Trataré de describir el procedimiento aquí:
1) Encuentra la causa de la falla. Desde el código de ejemplo, se ve que la memoria de "exampleString" está dañada y, por lo tanto, no se puede escribir en ella. Continuemos con esta suposición.
2) Establezca un punto de interrupción en la última ubicación conocida donde se use o modifique "exampleString" sin ningún problema.
3) Agregue un punto de observación al miembro de datos de ''exampleString''. Con mi versión de g ++, la cadena se almacena en _M_dataplus._M_p
. Queremos saber cuándo cambia este miembro de datos. La técnica de GDB para esto es:
(gdb) p &exampleString._M_dataplus._M_p
$3 = (char **) 0xbfccc2d8
(gdb) watch *$3
Hardware watchpoint 1: *$3
Obviamente estoy usando linux con g ++ y gdb aquí, pero creo que los puntos de observación de memoria están disponibles con la mayoría de los depuradores.
4) Continúe hasta que se active el punto de observación:
Continuing.
Hardware watchpoint 2: *$3
Old value = 0xb7ec2604 ""
New value = 0x804a014 ""
0xb7e70a1c in std::string::_M_mutate () from /usr/lib/libstdc++.so.6
(gdb) where
El comando gdb where
dará un seguimiento posterior que muestre lo que resultó en la modificación. Esta es una modificación perfectamente legal, en cuyo caso simplemente continúe, o si tiene suerte será la modificación debido a la corrupción de la memoria. En este último caso, ahora debería poder revisar el código que realmente está causando el problema y, con suerte, solucionarlo.
La causa de nuestro error fue un acceso de matriz con un índice negativo. El índice fue el resultado de un molde de un puntero a un modulo ''int'' del tamaño de la matriz. El error fue omitido por Valgrind et al. ya que las direcciones de memoria asignadas al ejecutarse bajo esas herramientas nunca fueron " > MAX_INT
" y nunca dieron como resultado un índice negativo.
Estos son mecanismos relativamente baratos para posiblemente resolver el problema:
- Mantenga un ojo en mi pregunta de corrupción de montón : estoy actualizando con las respuestas mientras se sacuden. El primero fue equilibrar
new[]
ydelete[]
, pero ya lo estás haciendo. - Dale más valor a valgrind ; es una herramienta excelente, y solo desearía que estuviera disponible en Windows. Solo ralentizo tu programa a la mitad, lo cual es bastante bueno en comparación con los equivalentes de Windows.
- Piensa en usar Google Performance Tools como un reemplazo de malloc / new.
- ¿Has limpiado todos tus archivos de objeto y vuelto a empezar? Tal vez su archivo make es ... "subóptimo"
- No estás
assert()
suficiente en tu código. ¿Cómo sé eso sin haberlo visto? Al igual que el hilo dental, nadieassert()
suficiente en su código. Agregue una función de validación para sus objetos y llámelo en el inicio del método y el final del método. - ¿Estás compilando -wall ? Si no, hazlo.
- Búscate una herramienta de pelusa como PC-Lint . ¡Una pequeña aplicación como la suya puede caber en la página de demostración de PC-lint , lo que significa que no hay compra para usted!
- Compruebe que está anulando punteros después de eliminarlos. A nadie le gusta un puntero colgante. Mismo concierto con punteros declarados pero no asignados.
- Deja de usar matrices. Use un vector en su lugar.
- No use punteros crudos. Use un puntero inteligente . ¡No use
auto_ptr
! Esa cosa es ... sorprendente; su semántica es muy extraña. En cambio, elija uno de los punteros inteligentes Boost , o algo fuera de la biblioteca Loki .
Tu código como puedo ver no tiene errores. Como se ha dicho, se necesita más contexto.
Si aún no lo ha intentado, instale gdb (el depurador gcc) y compile el programa con -g. Esto compilará en símbolos de depuración que puede usar gdb. Una vez que haya instalado gdb, ejecútelo con el programa (gdb). Este es un trucos útil para usar gdb.
Establezca un punto de interrupción para la función que produce el error y vea cuál es el valor de exampleString. Haga también lo mismo para cualquier parámetro que esté pasando a exampleString. Esto al menos debería decirle si las std :: cadenas son válidas.
La respuesta de este artículo me pareció una buena guía sobre punteros.