technologies software mexico descargar autopartes delphi

software - delphi technologies



¿Qué se supone que debe suceder al usar un objeto después de FreeAndNil? (6)

En mi Delphi7 este código

var MStr: TMemoryStream; ... FreeAndNil(MStr); MStr.Size:=0;

genera un AV: infracción de acceso en la dirección 0041D6D1 en el módulo ''Project1.exe''. Lectura de la dirección 00000000. Pero alguien insiste en que no debe plantear ninguna excepción, pase lo que pase. También dice que su Delphi 5 no plantea excepciones. Él llama a esto un "error del puntero rancio". En otras palabras, dice que FreeAndNil no se puede usar como depurador para detectar un doble intento de liberar un objeto o usar un objeto liberado.

¿Alguien puede iluminarme? ¿Debería aumentar y fallar (siempre / aleatoriamente) o el programa debería ejecutar este error sin problemas?

Gracias

Pregunto esto porque creo que tengo un error de "doble objeto libre" o "libre y de acceso" en mi programa. ¿Cómo puedo llenar la memoria asignada a un objeto con ceros DESPUÉS de haber liberado el objeto? Quiero esta forma de detectar dónde está el error, obteniendo AV. Inicialmente, esperaba que si establecía el objeto en FreeAndNil, SIEMPRE obtendría un AV cuando intentaba volver a acceder a él.


Por lo que estoy viendo, este código siempre debería generar un error. FreeAndNil establece explícitamente ese valor pasado a Nil (también conocido como 0), por lo que debe obtener una violación de acceso al intentar desreferenciar el objeto.


Si configura un puntero a cero, no debería poder usarlo más. Pero si tiene otro puntero al mismo objeto, puede usarlo sin obtener un AV, ya que este apunta a la dirección del objeto y no a cero.

Además, liberar un objeto no borra la memoria utilizada por ese objeto. Simplemente lo marca como no en uso. Esa es la razón por la que quieres obtener un AV. Si la memoria liberada se asigna para otro objeto, obtendrá un AV, porque ya no contiene datos que parecen válidos.

FastMM4 tiene algunas configuraciones que puede usar durante la depuración, que detectarán tales condiciones. Desde el FsatMM4Options.inc:

{Establezca la siguiente opción para realizar una verificación exhaustiva de todos los bloques de memoria. Todos los bloques están rellenos con un encabezado y un tráiler que se utilizan para verificar la integridad del montón. Los bloques liberados también se borran para garantizar que no puedan reutilizarse después de ser liberados. Esta opción ralentiza las operaciones de memoria de forma espectacular y solo debe utilizarse para depurar una aplicación que sobrescribe la memoria o reutiliza punteros liberados. La configuración de esta opción activa automáticamente CheckHeapForCorruption y deshabilita ASMVersion. Muy importante: si habilita esta opción, su aplicación requerirá la biblioteca FastMM_FullDebugMode.dll. Si esta biblioteca no está disponible, se producirá un error al iniciar.}
{$ define FullDebugMode}

Otra cita del mismo archivo:

FastMM siempre detecta intentos de liberar el mismo bloque de memoria dos veces ...

Como Delphi usa FastMM de Delphi 2007 (2006?), Debería obtener un error si intenta duplicar un objeto.


Solo para complicar el problema:

Si el método que llama es un método estático (no virtual) y no llama a ningún método virtual en sí mismo ni accede a ningún campo del objeto, no obtendrá una infracción de acceso incluso si la referencia del objeto se ha establecido en NIL.

La razón de esto es que la infracción de acceso es causada por la desreferenciación del puntero auto (en este caso NIL), pero eso solo ocurre cuando se accede a un campo o al VMT del objeto para llamar a un método virtual.

Esto es solo una excepción a la regla de que no puede llamar a los métodos de una referencia de objeto NIL que me gustaría mencionar aquí.


Siempre es incorrecto utilizar métodos o propiedades de una referencia nula, incluso si parece funcionar a veces.

FreeAndNil hecho, no se puede usar para detectar doble frecuencia. Es seguro llamar a FreeAndNil en una variable que ya no FreeAndNil . Como es seguro, no ayuda a detectar nada.

Esto no es un error de puntero rancio. Este es un error de referencia nula. Un error de puntero rancio es cuando liberaste un objeto pero no borraste todas las variables que lo referenciaron. Entonces la variable aún conserva la dirección anterior del objeto. Esos son muy difíciles de detectar. Puedes obtener un error como este:

MStr := TMemoryStream.Create; MStr.Free; MStr.Size := 0;

También puede obtener uno como este:

MStr := TMemoryStream.Create; OtherStr := MStr; FreeAndNil(MStr); OtherStr.Size := 0;

El uso de MStr.Size después de haber liberado el objeto al que se MStr referencia en MStr es un error, y debería generar una excepción. Si se plantea una excepción depende de la implementación. Tal vez lo haga, y tal vez no sea así. Sin embargo, no es aleatorio.

Si está buscando un error doblemente libre, puede usar los asistentes de depuración que proporciona FastMM, como otros han sugerido también. Funciona al no liberar la memoria de nuevo al sistema operativo, o incluso volver al grupo interno de memoria libre de Delphi. En su lugar, escribe datos mal conocidos en el espacio de memoria del objeto, por lo que cuando vea esos valores, sabrá que está leyendo algo que ya ha liberado. También modifica el VMT del objeto para que la próxima vez que llame a un método virtual en esa referencia de objeto, obtenga una excepción predecible, e incluso le dirá qué objeto supuestamente liberado intentó usar. Cuando intenta liberar el objeto nuevamente, puede indicarle no solo que ya lo liberó, sino también dónde fue liberado la primera vez (con un seguimiento de la pila) y dónde fue asignado. También recopila esa información para informar sobre fugas de memoria, donde liberaste un objeto menos de una vez en lugar de más.

También hay hábitos que puede usar para evitar el problema del código futuro:

  • Reduzca el uso de variables globales. Una variable global podría ser modificada por cualquier código a lo largo del programa, lo que le obligará a preguntarse cada vez que lo use: "¿El valor de esta variable sigue siendo válido o algún otro código ya lo ha liberado?" Cuando limita el alcance de una variable, reduce la cantidad de código que debe considerar en su programa cuando busca razones por las cuales una variable no tiene el valor que espera.
  • Tenga en claro quién posee un objeto. Cuando hay dos piezas de código que tienen acceso al mismo objeto, necesita saber cuál de esas piezas de código posee el objeto. Es posible que cada uno tenga una variable diferente para hacer referencia al objeto, pero todavía hay un solo objeto allí. Si un fragmento de código llama a FreeAndNil en su variable, aún deja la variable del otro código sin cambios. Si ese otro código cree que posee el objeto, entonces estás en problemas. (Este concepto de propietario no está necesariamente vinculado a la propiedad TComponent.Owner . No tiene que ser un objeto que lo posea, podría ser un subsistema general de su programa).
  • No mantenga referencias persistentes a objetos que no le pertenecen. Si no mantiene referencias de larga duración a un objeto, entonces no tiene que preocuparse de si esas referencias siguen siendo válidas. La única referencia persistente debe estar en el código que posee el objeto. Cualquier otro código que necesite usar ese objeto debería recibir una referencia como parámetro de entrada, usar el objeto y luego descartar la referencia cuando devuelve su resultado.

Thomas Mueller : ¿has probado los métodos de clase virtual? Un constructor es una especie de método virtual, pero lo llamas contra el tipo, no contra la instancia. Esto significa que incluso algunos métodos virtuales específicos no causarán AV en una referencia nula: D

Vegar : ¡No podrías tener más razón! FastMM es la mejor herramienta de todos los tiempos que me ayudó a rastrear este tipo de errores.