Interesante

Comprensión de la asignación de memoria en Delphi

Comprensión de la asignación de memoria en Delphi

Llame a la función "DoStackOverflow" una vez desde su código y obtendrá el EStackOverflow error provocado por Delphi con el mensaje "desbordamiento de pila".

función DoStackOverflow: entero;

empezar

resultado: = 1 + DoStackOverflow;

fin;

¿Qué es esta "pila" y por qué hay un desbordamiento usando el código anterior?

Entonces, la función DoStackOverflow se llama a sí misma recursivamente, sin una "estrategia de salida", simplemente sigue girando y nunca se cierra.

Una solución rápida, lo haría, es eliminar el error obvio que tiene y asegurarse de que la función exista en algún momento (para que su código pueda continuar ejecutándose desde donde la llamó).

Sigues adelante y nunca miras hacia atrás, sin preocuparte por el error / excepción, ya que ahora está resuelto.

Sin embargo, la pregunta sigue siendo: ¿Qué es esta pila y por qué hay un desbordamiento??

Memoria en sus aplicaciones de Delphi

Cuando comience a programar en Delphi, puede experimentar un error como el anterior, lo resolverá y continuará. Este está relacionado con la asignación de memoria. La mayoría de las veces no le importaría la asignación de memoria siempre que libere lo que crea.

A medida que adquiere más experiencia en Delphi, comienza a crear sus propias clases, las instancia, se preocupa por la gestión de la memoria y similares.

Llegará al punto donde leerá, en la Ayuda, algo como "Las variables locales (declaradas dentro de los procedimientos y funciones) residen en la aplicación apilar." y también Las clases son tipos de referencia, por lo que no se copian en la asignación, se pasan por referencia y se asignan en el montón.

Entonces, ¿qué es "apilar" y qué es "montón"?

Pila contra montón

Al ejecutar su aplicación en Windows, hay tres áreas en la memoria donde su aplicación almacena datos: memoria global, montón y pila.

Las variables globales (sus valores / datos) se almacenan en la memoria global. La aplicación reserva la memoria para las variables globales cuando se inicia el programa y permanece asignada hasta que finaliza el programa. La memoria para las variables globales se llama "segmento de datos".

Dado que la memoria global solo se asigna y libera una vez al finalizar el programa, no nos importa en este artículo.

La pila y el montón son donde tiene lugar la asignación dinámica de memoria: cuando crea una variable para una función, cuando crea una instancia de una clase cuando envía parámetros a una función y usa / pasa su valor de resultado.

¿Qué es la pila?

Cuando declara una variable dentro de una función, la memoria requerida para contener la variable se asigna desde la pila. Simplemente escriba "var x: integer", use "x" en su función, y cuando la función salga, no le importará la asignación de memoria ni la liberación. Cuando la variable se sale del alcance (el código sale de la función), se libera la memoria que se tomó en la pila.

La memoria de la pila se asigna dinámicamente utilizando el enfoque LIFO ("último en entrar, primero en salir").

En los programas de Delphi, la memoria de pila es utilizada por

  • Variables locales de rutina (método, procedimiento, función).
  • Parámetros de rutina y tipos de retorno.
  • Llamadas de función API de Windows.
  • Registros (es por eso que no tiene que crear explícitamente una instancia de un tipo de registro).

No tiene que liberar explícitamente la memoria en la pila, ya que la memoria se asigna automáticamente por arte de magia cuando, por ejemplo, declara una variable local a una función. Cuando la función se cierra (a veces incluso antes debido a la optimización del compilador Delphi), la memoria para la variable se liberará automáticamente.

El tamaño de la memoria de la pila es, por defecto, lo suficientemente grande para sus programas Delphi (tan complejos como son). Los valores de "Tamaño máximo de pila" y "Tamaño mínimo de pila" en las opciones de Linker para su proyecto especifican valores predeterminados: en 99.99% no necesitaría modificar esto.

Piense en una pila como una pila de bloques de memoria. Cuando declara / usa una variable local, el administrador de memoria de Delphi elegirá el bloque desde la parte superior, lo usará y, cuando ya no sea necesario, volverá a la pila.

Al utilizar la memoria de variables locales de la pila, las variables locales no se inicializan cuando se declaran. Declare una variable "var x: integer" en alguna función e intente leer el valor cuando ingrese la función: x tendrá algún valor "extraño" distinto de cero. Por lo tanto, siempre inicialice (o establezca el valor) en sus variables locales antes de leer su valor.

Debido a LIFO, las operaciones de pila (asignación de memoria) son rápidas ya que solo se requieren unas pocas operaciones (push, pop) para administrar una pila.

¿Qué es el montón?

Un montón es una región de memoria en la que se almacena memoria asignada dinámicamente. Cuando crea una instancia de una clase, la memoria se asigna desde el montón.

En los programas de Delphi, la memoria de almacenamiento dinámico es utilizada por / cuando

  • Crear una instancia de una clase.
  • Creación y cambio de tamaño de matrices dinámicas.
  • Asignación explícita de memoria usando GetMem, FreeMem, New y Dispose ().
  • Utilizando cadenas ANSI / wide / Unicode, variantes, interfaces (gestionadas automáticamente por Delphi).

La memoria de almacenamiento dinámico no tiene un diseño agradable en el que habría algún orden al asignar bloques de memoria. El montón parece una lata de canicas. La asignación de memoria del montón es aleatoria, un bloque desde aquí que un bloque desde allí. Por lo tanto, las operaciones de almacenamiento dinámico son un poco más lentas que las de la pila.

Cuando solicite un nuevo bloque de memoria (es decir, cree una instancia de una clase), el administrador de memoria de Delphi se encargará de esto por usted: obtendrá un nuevo bloque de memoria o uno usado y descartado.

El montón consta de toda la memoria virtual (RAM y espacio en disco).

Asignación manual de memoria

Ahora que todo está claro sobre la memoria, puede ignorar de manera segura (en la mayoría de los casos) lo anterior y simplemente continuar escribiendo programas de Delphi como lo hizo ayer.

Por supuesto, debe saber cuándo y cómo asignar / liberar memoria manualmente.

El "EStackOverflow" (desde el principio del artículo) se planteó porque con cada llamada a DoStackOverflow se ha utilizado un nuevo segmento de memoria de la pila y la pila tiene limitaciones. Tan sencillo como eso.