Pregunta

Los libros sobre lenguajes de programación explican que los tipos de valores se crean en el pila, y los tipos de referencia se crean en el montón, sin explicar cuáles son estas dos cosas.No he leído una explicación clara de esto.entiendo lo que un montón es.Pero,

  • ¿Dónde y qué están (físicamente en la memoria de una computadora real)?
  • ¿Hasta qué punto están controlados por el sistema operativo o el tiempo de ejecución del lenguaje?
  • ¿Cuál es su alcance?
  • ¿Qué determina el tamaño de cada uno de ellos?
  • ¿Qué hace que uno sea más rápido?
¿Fue útil?

Solución

La pila es la memoria reservada como espacio temporal para un hilo de ejecución.Cuando se llama a una función, se reserva un bloque en la parte superior de la pila para variables locales y algunos datos contables.Cuando esa función regresa, el bloque deja de usarse y puede usarse la próxima vez que se llame a una función.La pila siempre está reservada en orden LIFO (último en entrar, primero en salir);el bloque reservado más recientemente es siempre el siguiente bloque que se liberará.Esto hace que sea realmente sencillo realizar un seguimiento de la pila;liberar un bloque de la pila no es más que ajustar un puntero.

El montón es memoria reservada para la asignación dinámica.A diferencia de la pila, no existe un patrón obligatorio para la asignación y desasignación de bloques del montón;puedes asignar un bloque en cualquier momento y liberarlo en cualquier momento.Esto hace que sea mucho más complejo realizar un seguimiento de qué partes del montón están asignadas o libres en un momento dado;Hay muchos asignadores de montón personalizados disponibles para ajustar el rendimiento del montón para diferentes patrones de uso.

Cada subproceso obtiene una pila, mientras que normalmente solo hay un montón para la aplicación (aunque no es raro tener varios montones para diferentes tipos de asignación).

Para responder a sus preguntas directamente:

¿Hasta qué punto están controlados por el sistema operativo o el tiempo de ejecución del lenguaje?

El sistema operativo asigna la pila para cada subproceso a nivel del sistema cuando se crea el subproceso.Normalmente, el tiempo de ejecución del lenguaje llama al sistema operativo para asignar el montón para la aplicación.

¿Cuál es su alcance?

La pila está adjunta a un subproceso, por lo que cuando el subproceso sale, la pila se recupera.El tiempo de ejecución generalmente asigna el montón al inicio de la aplicación y se recupera cuando la aplicación (técnicamente proceso) sale.

¿Qué determina el tamaño de cada uno de ellos?

El tamaño de la pila se establece cuando se crea un hilo.El tamaño del montón se establece al iniciar la aplicación, pero puede crecer a medida que se necesita espacio (el asignador solicita más memoria del sistema operativo).

¿Qué hace que uno sea más rápido?

La pila es más rápida porque el patrón de acceso hace que sea trivial asignar y desasignar memoria (un puntero/entero simplemente se incrementa o disminuye), mientras que el montón tiene una contabilidad mucho más compleja involucrada en una asignación o desasignación.Además, cada byte de la pila tiende a reutilizarse con mucha frecuencia, lo que significa que tiende a asignarse a la memoria caché del procesador, lo que lo hace muy rápido.Otro impacto en el rendimiento del montón es que el montón, al ser principalmente un recurso global, normalmente tiene que ser seguro para múltiples subprocesos, es decir.cada asignación y desasignación debe sincronizarse, normalmente, con "todos" los demás accesos al montón del programa.

Una demostración clara:
Fuente de imagen: vikashazrati.wordpress.com

Otros consejos

Pila:

  • Almacenado en la RAM de la computadora como el montón.
  • Las variables creadas en la pila quedarán fuera de alcance y se desasignarán automáticamente.
  • Mucho más rápido de asignar en comparación con las variables del montón.
  • Implementado con una estructura de datos de pila real.
  • Almacena datos locales, direcciones de retorno, que se utilizan para pasar parámetros.
  • Puede tener un desbordamiento de pila cuando se usa demasiada pila (principalmente debido a una recursividad infinita o demasiado profunda, asignaciones muy grandes).
  • Los datos creados en la pila se pueden utilizar sin punteros.
  • Utilizaría la pila si sabe exactamente cuántos datos necesita asignar antes del tiempo de compilación y si no es demasiado grande.
  • Generalmente tiene un tamaño máximo ya determinado cuando se inicia el programa.

Montón:

  • Almacenado en la RAM de la computadora al igual que la pila.
  • En C++, las variables en el montón deben destruirse manualmente y nunca quedar fuera de alcance.Los datos se liberan con delete, delete[], o free.
  • Más lento de asignar en comparación con las variables de la pila.
  • Se utiliza bajo demanda para asignar un bloque de datos para uso del programa.
  • Puede haber fragmentación cuando hay muchas asignaciones y desasignaciones.
  • En C++ o C, los datos creados en el montón serán señalados mediante punteros y asignados con new o malloc respectivamente.
  • Puede tener errores de asignación si se solicita asignar un búfer demasiado grande.
  • Utilizaría el montón si no sabe exactamente cuántos datos necesitará en tiempo de ejecución o si necesita asignar muchos datos.
  • Responsable de pérdidas de memoria.

Ejemplo:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;

El punto más importante es que montón y pila son términos genéricos que describen las formas en que se puede asignar la memoria.Se pueden implementar de muchas maneras diferentes y los términos se aplican a los conceptos básicos.

  • En una pila de elementos, los elementos se colocan uno encima del otro en el orden en que fueron colocados allí, y solo puedes quitar el de arriba (sin volcar todo).

    Stack like a stack of papers

    La simplicidad de una pila es que no es necesario mantener una tabla que contenga un registro de cada sección de memoria asignada;la única información de estado que necesita es un único puntero al final de la pila.Para asignar y desasignar, simplemente incrementa y disminuye ese único puntero.Nota:A veces se puede implementar una pila para comenzar en la parte superior de una sección de memoria y extenderse hacia abajo en lugar de crecer hacia arriba.

  • En un montón, no existe un orden particular en la forma en que se colocan los elementos.Puede acceder y eliminar elementos en cualquier orden porque no hay un elemento "superior" claro.

    Heap like a heap of licorice allsorts

    La asignación de montón requiere mantener un registro completo de qué memoria está asignada y cuál no, así como algunos gastos generales de mantenimiento para reducir la fragmentación, encontrar segmentos de memoria contiguos lo suficientemente grandes como para ajustarse al tamaño solicitado, etc.La memoria se puede desasignar en cualquier momento dejando espacio libre.A veces, un asignador de memoria realizará tareas de mantenimiento, como desfragmentar la memoria moviendo la memoria asignada o recolectando basura, identificando en tiempo de ejecución cuándo la memoria ya no está dentro del alcance y desasignándola.

Estas imágenes deberían describir bastante bien las dos formas de asignar y liberar memoria en una pila y un montón.¡Mmm!

  • ¿Hasta qué punto están controlados por el sistema operativo o el tiempo de ejecución del lenguaje?

    Como se mencionó, montón y pila son términos generales y se pueden implementar de muchas maneras.Los programas informáticos suelen tener una pila llamada pila de llamadas que almacena información relevante para la función actual, como un puntero a cualquier función desde la que se llamó y cualquier variable local.Debido a que las funciones llaman a otras funciones y luego regresan, la pila crece y se reduce para contener información de las funciones que se encuentran más abajo en la pila de llamadas.Un programa realmente no tiene control de tiempo de ejecución sobre él;está determinado por el lenguaje de programación, el sistema operativo e incluso la arquitectura del sistema.

    Un montón es un término general utilizado para cualquier memoria que se asigna de forma dinámica y aleatoria;es decir.fuera de servicio.La memoria normalmente la asigna el sistema operativo, y la aplicación llama a funciones API para realizar esta asignación.Se requiere una cierta sobrecarga para administrar la memoria asignada dinámicamente, que generalmente es manejada por el sistema operativo.

  • ¿Cuál es su alcance?

    La pila de llamadas es un concepto de tan bajo nivel que no se relaciona con el "alcance" en el sentido de programación.Si desensamblas algún código verás referencias relativas de estilo de puntero a partes de la pila, pero en lo que respecta a un lenguaje de nivel superior, el lenguaje impone sus propias reglas de alcance.Sin embargo, un aspecto importante de una pila es que una vez que una función regresa, cualquier elemento local de esa función se libera inmediatamente de la pila.Eso funciona de la forma esperada, dado cómo funcionan sus lenguajes de programación.En un montón, también es difícil de definir.El alcance es todo lo que expone el sistema operativo, pero su lenguaje de programación probablemente agregue sus reglas sobre qué es un "alcance" en su aplicación.La arquitectura del procesador y el sistema operativo utilizan direccionamiento virtual, que el procesador traduce a direcciones físicas y se producen fallos de página, etc.Realizan un seguimiento de qué páginas pertenecen a qué aplicaciones.Sin embargo, nunca necesita preocuparse por esto, porque simplemente usa cualquier método que utilice su lenguaje de programación para asignar y liberar memoria, y verificar si hay errores (si la asignación/liberación falla por algún motivo).

  • ¿Qué determina el tamaño de cada uno de ellos?

    Nuevamente, depende del idioma, el compilador, el sistema operativo y la arquitectura.Una pila generalmente está preasignada porque, por definición, debe ser memoria contigua (más sobre esto en el último párrafo).El compilador del lenguaje o el sistema operativo determinan su tamaño.No se almacenan grandes cantidades de datos en la pila, por lo que será lo suficientemente grande como para que nunca se utilice por completo, excepto en casos de recursividad interminable no deseada (por lo tanto, "desbordamiento de pila") u otras decisiones de programación inusuales.

    Un montón es un término general para cualquier cosa que pueda asignarse dinámicamente.Dependiendo de cómo se mire, cambia constantemente de tamaño.De todos modos, en los procesadores y sistemas operativos modernos, la forma exacta en que funciona es muy abstracta, por lo que normalmente no necesita preocuparse mucho por cómo funciona en el fondo, excepto que (en los idiomas donde lo permite) no debe usar memoria que que aún no has asignado o memoria que has liberado.

  • ¿Qué hace que uno sea más rápido?

    La pila es más rápida porque toda la memoria libre es siempre contigua.No es necesario mantener una lista de todos los segmentos de memoria libre, solo un puntero a la parte superior actual de la pila.Los compiladores suelen almacenar este puntero en un archivo especial y rápido. registro para este propósito.Es más, las operaciones posteriores en una pila generalmente se concentran en áreas de memoria muy cercanas, lo que a un nivel muy bajo es bueno para la optimización por parte de las cachés integradas del procesador.

(He movido esta respuesta de otra pregunta que fue más o menos una trampa de esta).

La respuesta a su pregunta es específica de la implementación y puede variar según los compiladores y arquitecturas de procesador.Sin embargo, aquí hay una explicación simplificada.

  • Tanto la pila como el montón son áreas de memoria asignadas desde el sistema operativo subyacente (a menudo, memoria virtual que se asigna a la memoria física según demanda).
  • En un entorno de subprocesos múltiples, cada subproceso tendrá su propia pila completamente independiente, pero compartirán el montón.El acceso simultáneo debe controlarse en el montón y no es posible en la pila.

el montón

  • El montón contiene una lista vinculada de bloques usados ​​y gratuitos.Nuevas asignaciones en el montón (por new o malloc) se satisfacen creando un bloque adecuado a partir de uno de los bloques libres.Esto requiere actualizar la lista de bloques en el montón.Este meta informacion La información sobre los bloques en el montón también se almacena en el montón, a menudo en un área pequeña justo en frente de cada bloque.
  • A medida que el montón crece, a menudo se asignan nuevos bloques desde direcciones inferiores hacia direcciones superiores.Por lo tanto, puedes pensar en el montón como un montón de bloques de memoria que crece en tamaño a medida que se asigna memoria.Si el montón es demasiado pequeño para una asignación, el tamaño a menudo se puede aumentar adquiriendo más memoria del sistema operativo subyacente.
  • La asignación y desasignación de muchos bloques pequeños puede dejar el montón en un estado en el que hay muchos bloques pequeños libres intercalados entre los bloques usados.Una solicitud para asignar un bloque grande puede fallar porque ninguno de los bloques libres es lo suficientemente grande para satisfacer la solicitud de asignación, aunque el tamaño combinado de los bloques libres pueda ser lo suficientemente grande.Se llama fragmentación del montón.
  • Cuando se desasigna un bloque usado adyacente a un bloque libre, el nuevo bloque libre se puede fusionar con el bloque libre adyacente para crear un bloque libre más grande, lo que reduce efectivamente la fragmentación del montón.

The heap

La pila

  • La pila a menudo funciona en conjunto con un registro especial en la CPU llamado puntero de pila.Inicialmente, el puntero de la pila apunta a la parte superior de la pila (la dirección más alta de la pila).
  • La CPU tiene instrucciones especiales para emprendedor valores en la pila y estallando retírelos de la pila.Cada empujar almacena el valor en la ubicación actual del puntero de la pila y disminuye el puntero de la pila.A estallido recupera el valor señalado por el puntero de la pila y luego aumenta el puntero de la pila (no se deje confundir por el hecho de que agregando un valor a la pila disminuye el puntero de la pila y eliminando un valor aumenta él.Recuerde que la pila crece hasta el fondo).Los valores almacenados y recuperados son los valores de los registros de la CPU.
  • Cuando se llama a una función, la CPU utiliza instrucciones especiales que impulsan la corriente puntero de instrucción, es decir.la dirección del código que se ejecuta en la pila.La CPU luego salta a la función configurando el puntero de instrucciones en la dirección de la función llamada.Más tarde, cuando la función regresa, el antiguo puntero de instrucción se extrae de la pila y la ejecución se reanuda en el código justo después de la llamada a la función.
  • Cuando se ingresa una función, el puntero de la pila disminuye para asignar más espacio en la pila para variables locales (automáticas).Si la función tiene una variable local de 32 bits, se reservan cuatro bytes en la pila.Cuando la función regresa, el puntero de la pila se mueve hacia atrás para liberar el área asignada.
  • Si una función tiene parámetros, estos se colocan en la pila antes de llamar a la función.Luego, el código de la función puede navegar hacia arriba en la pila desde el puntero de la pila actual para localizar estos valores.
  • Las llamadas a funciones de anidamiento funcionan a las mil maravillas.Cada nueva llamada asignará parámetros de función, la dirección de retorno y el espacio para las variables locales y estos registros de activación se puede apilar para llamadas anidadas y se desenrollará de la manera correcta cuando regresen las funciones.
  • Como la pila es un bloque de memoria limitado, puede provocar un desbordamiento de pila llamando a demasiadas funciones anidadas y/o asignando demasiado espacio para variables locales.A menudo, el área de memoria utilizada para la pila se configura de tal manera que escribir debajo de la parte inferior (la dirección más baja) de la pila desencadenará una trampa o excepción en la CPU.El tiempo de ejecución puede detectar esta condición excepcional y convertirla en algún tipo de excepción de desbordamiento de pila.

The stack

¿Se puede asignar una función en el montón en lugar de en una pila?

No, registros de activación de funciones (es decir,variables locales o automáticas) se asignan en la pila que se utiliza no sólo para almacenar estas variables, sino también para realizar un seguimiento de las llamadas a funciones anidadas.

La forma en que se administra el montón depende realmente del entorno de ejecución.C usos malloc y usos de C++ new, pero muchos otros idiomas tienen recolección de basura.

Sin embargo, la pila es una característica de nivel más bajo estrechamente ligada a la arquitectura del procesador.Hacer crecer el montón cuando no hay suficiente espacio no es demasiado difícil ya que se puede implementar en la llamada a la biblioteca que maneja el montón.Sin embargo, hacer crecer la pila suele ser imposible ya que el desbordamiento de la pila sólo se descubre cuando ya es demasiado tarde;y cerrar el hilo de ejecución es la única opción viable.

En el siguiente código C#

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

Así se gestiona la memoria

Picture of variables on the stack

Local Variables eso solo necesita durar mientras la invocación de la función esté en la pila.El montón se utiliza para variables cuya vida útil no conocemos realmente desde el principio, pero esperamos que duren un tiempo.En la mayoría de los lenguajes es fundamental que sepamos en el momento de la compilación qué tan grande es una variable si queremos almacenarla en la pila.

Los objetos (que varían en tamaño a medida que los actualizamos) van al montón porque no sabemos en el momento de la creación cuánto tiempo van a durar.En muchos idiomas, el montón se recolecta como basura para encontrar objetos (como el objeto cls1) que ya no tienen referencias.

En Java, la mayoría de los objetos van directamente al montón.En lenguajes como C/C++, las estructuras y clases a menudo pueden permanecer en la pila cuando no se trata de punteros.

Puede encontrar más información aquí:

La diferencia entre la asignación de memoria de pila y de montón «timmurphy.org

y aquí:

Crear objetos en la pila y el montón

Este artículo es la fuente de la imagen de arriba: Seis conceptos importantes de .NET:Pila, montón, tipos de valor, tipos de referencia, boxeo y unboxing - CodeProject

pero tenga en cuenta que puede contener algunas inexactitudes.

La pilaCuando llamas a una función, los argumentos de esa función más algunos otros gastos generales se colocan en la pila.Parte de la información (como adónde ir al regresar) también se almacena allí.Cuando declaras una variable dentro de tu función, esa variable también se asigna en la pila.

Desasignar la pila es bastante simple porque siempre se desasigna en el orden inverso al de la asignación.Las cosas de la pila se agregan a medida que ingresa funciones, los datos correspondientes se eliminan al salir de ellas.Esto significa que tiende a permanecer dentro de una pequeña región de la pila a menos que llame a muchas funciones que llaman a muchas otras funciones (o cree una solución recursiva).

el montónEl montón es un nombre genérico para el lugar donde colocas los datos que creas sobre la marcha.Si no sabe cuántas naves espaciales va a crear su programa, es probable que utilice el operador nuevo (o malloc o equivalente) para crear cada nave espacial.Esta asignación se mantendrá por un tiempo, por lo que es probable que liberemos cosas en un orden diferente al que las creamos.

Por lo tanto, el montón es mucho más complejo, porque termina habiendo regiones de memoria que no se utilizan entrelazadas con fragmentos que sí lo están: la memoria se fragmenta.Encontrar memoria libre del tamaño que necesita es un problema difícil.Es por eso que se debe evitar el montón (aunque todavía se usa con frecuencia).

ImplementaciónLa implementación tanto de la pila como del montón generalmente depende del tiempo de ejecución/sistema operativo.A menudo, los juegos y otras aplicaciones que son críticas para el rendimiento crean sus propias soluciones de memoria que toman una gran cantidad de memoria del montón y luego la distribuyen internamente para evitar depender del sistema operativo para la memoria.

Esto sólo es práctico si el uso de tu memoria es bastante diferente de lo normal, es decir, para juegos en los que cargas un nivel en una operación enorme y puedes tirarlo todo en otra operación enorme.

Ubicación física en la memoria.Esto es menos relevante de lo que piensas debido a una tecnología llamada Memoria virtual lo que hace que su programa piense que tiene acceso a una determinada dirección donde los datos físicos están en otro lugar (¡incluso en el disco duro!).Las direcciones que obtienes para la pila están en orden creciente a medida que tu árbol de llamadas se hace más profundo.Las direcciones del montón son impredecibles (es decir, específicas de implementación) y, francamente, no son importantes.

Para aclarar, esta respuesta tiene información incorrecta (tomás arregló su respuesta después de los comentarios, genial :)).Otras respuestas simplemente evitan explicar qué significa la asignación estática.Por lo tanto, a continuación explicaré las tres formas principales de asignación y cómo se relacionan generalmente con el montón, la pila y el segmento de datos.También mostraré algunos ejemplos tanto en C/C++ como en Python para ayudar a la gente a comprender.

Las variables "estáticas" (también conocidas como asignadas estáticamente) no se asignan en la pila.No lo asuma, muchas personas lo hacen sólo porque "estático" suena muy parecido a "pila".En realidad, no existen ni en la pila ni en el montón.Son parte de lo que se llama segmento de datos.

Sin embargo, en general es mejor considerar "alcance" y "toda la vida" en lugar de "pila" y "montón".

El alcance se refiere a qué partes del código pueden acceder a una variable.Generalmente pensamos en alcance local (solo se puede acceder mediante la función actual) versus alcance global (se puede acceder desde cualquier lugar), aunque el alcance puede volverse mucho más complejo.

La vida útil se refiere a cuándo se asigna y desasigna una variable durante la ejecución del programa.Generalmente pensamos en asignación estática (la variable persistirá durante toda la duración del programa, lo que la hace útil para almacenar la misma información en varias llamadas a funciones) versus asignación automática (la variable solo persiste durante una única llamada a una función, lo que la hace útil para almacenar información que solo se usa durante la función y se puede descartar una vez que haya terminado) versus asignación dinámica (variables cuya duración se define en tiempo de ejecución, en lugar de tiempo de compilación como estática o automática).

Aunque la mayoría de los compiladores e intérpretes implementan este comportamiento de manera similar en términos de uso de pilas, montones, etc., un compilador a veces puede romper estas convenciones si así lo desea, siempre que el comportamiento sea correcto.Por ejemplo, debido a la optimización, es posible que una variable local solo exista en un registro o se elimine por completo, aunque la mayoría de las variables locales existan en la pila.Como se ha señalado en algunos comentarios, usted es libre de implementar un compilador que ni siquiera usa una pila o un montón, sino algunos otros mecanismos de almacenamiento (rara vez se hace, ya que las pilas y los montones son excelentes para esto).

Proporcionaré un código C anotado simple para ilustrar todo esto.La mejor manera de aprender es ejecutar un programa con un depurador y observar el comportamiento.Si prefieres leer Python, salta hasta el final de la respuesta :)

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

Un ejemplo particularmente conmovedor de por qué es importante distinguir entre duración y alcance es que una variable puede tener alcance local pero duración estática; por ejemplo, "someLocalStaticVariable" en el ejemplo de código anterior.Estas variables pueden hacer que nuestros hábitos comunes pero informales de denominación sean muy confusos.Por ejemplo cuando decimos "local"normalmente queremos decir"variable asignada automáticamente de ámbito local" y cuando decimos global normalmente queremos decir "variable asignada estáticamente de ámbito global".Desafortunadamente cuando se trata de cosas como "variables asignadas estáticamente con ámbito de archivo" Mucha gente simplemente dice..."¿¿¿eh???".

Algunas de las opciones de sintaxis en C/C++ exacerban este problema; por ejemplo, muchas personas piensan que las variables globales no son "estáticas" debido a la sintaxis que se muestra a continuación.

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

Tenga en cuenta que poner la palabra clave "estática" en la declaración anterior evita que var2 tenga alcance global.Sin embargo, la var1 global tiene una asignación estática.¡Esto no es intuitivo!Por esta razón, trato de no utilizar nunca la palabra "estático" al describir el alcance y, en su lugar, digo algo como "archivo" o alcance "limitado por archivo".Sin embargo, muchas personas usan la frase "estático" o "alcance estático" para describir una variable a la que solo se puede acceder desde un archivo de código.En el contexto de la vida, "estático" siempre significa que la variable se asigna al inicio del programa y se desasigna cuando el programa sale.

Algunas personas piensan que estos conceptos son específicos de C/C++.Ellos no son.Por ejemplo, el siguiente ejemplo de Python ilustra los tres tipos de asignación (existen algunas diferencias sutiles posibles en los lenguajes interpretados que no abordaré aquí).

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones

Otros han respondido bastante bien a grandes rasgos, así que daré algunos detalles.

  1. La pila y el montón no tienen por qué ser singulares.Una situación común en la que tienes más de una pila es si tienes más de un subproceso en un proceso.En este caso cada hilo tiene su propia pila.También puede tener más de un montón; por ejemplo, algunas configuraciones de DLL pueden dar lugar a que se asignen diferentes DLL desde diferentes montones, por lo que generalmente es una mala idea liberar memoria asignada por una biblioteca diferente.

  2. En C puede obtener el beneficio de la asignación de longitud variable mediante el uso de asignar, que asigna en la pila, a diferencia de alloc, que asigna en el montón.Esta memoria no sobrevivirá a su declaración de devolución, pero es útil como un búfer temporal.

  3. Crear un enorme búfer temporal en Windows que no usas mucho no es gratis.Esto se debe a que el compilador generará un bucle de sondeo de pila que se llama cada vez que se ingresa su función para asegurarse de que la pila exista (porque Windows usa una única página de protección al final de su pila para detectar cuándo necesita hacer crecer la pila.Si accede a la memoria a más de una página del final de la pila, se bloqueará).Ejemplo:

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}

Otros han respondido directamente a su pregunta, pero al intentar comprender la pila y el montón, creo que es útil considerar el diseño de la memoria de un proceso UNIX tradicional (sin subprocesos y mmap()-asignadores basados ​​en).El Glosario de gestión de memoria La página web tiene un diagrama de este diseño de memoria.

La pila y el montón se ubican tradicionalmente en extremos opuestos del espacio de direcciones virtuales del proceso.La pila crece automáticamente cuando se accede a ella, hasta un tamaño establecido por el kernel (que se puede ajustar con setrlimit(RLIMIT_STACK, ...)).El montón crece cuando el asignador de memoria invoca el brk() o sbrk() llamada al sistema, asignando más páginas de memoria física al espacio de direcciones virtuales del proceso.

En sistemas sin memoria virtual, como algunos sistemas integrados, a menudo se aplica el mismo diseño básico, excepto que la pila y el montón tienen un tamaño fijo.Sin embargo, en otros sistemas integrados (como los basados ​​en microcontroladores PIC de Microchip), la pila de programas es un bloque de memoria separado que no es direccionable mediante instrucciones de movimiento de datos y solo puede modificarse o leerse indirectamente a través de instrucciones de flujo de programa (llamada, devolución, etcétera).Otras arquitecturas, como los procesadores Intel Itanium, tienen múltiples pilas.En este sentido, la pila es un elemento de la arquitectura de la CPU.

La pila es una porción de la memoria que se puede manipular mediante varias instrucciones clave en lenguaje ensamblador, como 'pop' (eliminar y devolver un valor de la pila) y 'push' (empujar un valor a la pila), pero también llamar ( llamar a una subrutina - esto empuja la dirección para que regrese a la pila) y regresar (regresar desde una subrutina - esto saca la dirección de la pila y salta a ella).Es la región de memoria debajo del registro del puntero de la pila, que se puede configurar según sea necesario.La pila también se utiliza para pasar argumentos a subrutinas y también para preservar los valores en los registros antes de llamar a las subrutinas.

El montón es una porción de memoria que el sistema operativo le da a una aplicación, generalmente a través de una llamada al sistema como malloc.En los sistemas operativos modernos, esta memoria es un conjunto de páginas a las que sólo tiene acceso el proceso que realiza la llamada.

El tamaño de la pila se determina en tiempo de ejecución y, por lo general, no crece después de que se inicia el programa.En un programa C, la pila debe ser lo suficientemente grande como para contener todas las variables declaradas dentro de cada función.El montón crecerá dinámicamente según sea necesario, pero el sistema operativo es quien finalmente realiza la llamada (a menudo aumentará el montón más que el valor solicitado por malloc, de modo que al menos algunos mallocs futuros no necesitarán volver al kernel para obtener más memoria.Este comportamiento suele ser personalizable)

Debido a que ha asignado la pila antes de iniciar el programa, nunca necesita realizar malloc antes de poder usar la pila, por lo que esa es una ligera ventaja.En la práctica, es muy difícil predecir qué será rápido y qué será lento en los sistemas operativos modernos que tienen subsistemas de memoria virtual, porque cómo se implementan las páginas y dónde se almacenan es un detalle de implementación.

Creo que muchas otras personas le han dado respuestas en su mayoría correctas sobre este asunto.

Un detalle que se ha pasado por alto, sin embargo, es que el "montón" probablemente debería llamarse "tienda gratuita".La razón de esta distinción es que la tienda gratuita original se implementó con una estructura de datos conocida como un "montón binomial". Por esa razón, la asignación de implementaciones tempranas de Malloc ()/Free () fue la asignación de un montón.Sin embargo, hoy en día, la mayoría de las tiendas gratuitas se implementan con estructuras de datos muy elaboradas que no son montones binomiales.

¿Qué es una pila?

Una pila es una pila de objetos, normalmente uno que está cuidadosamente ordenado.

Enter image description here

Las pilas en las arquitecturas informáticas son regiones de la memoria donde los datos se agregan o eliminan según el último en entrar, el primero en salir.
En una aplicación multiproceso, cada subproceso tendrá su propia pila.

¿Qué es un montón?

Un montón es una colección desordenada de cosas amontonadas al azar.

Enter image description here

En las arquitecturas informáticas, el montón es un área de memoria asignada dinámicamente que el sistema operativo o la biblioteca del administrador de memoria administra automáticamente.
La memoria en el montón se asigna, desasigna y cambia de tamaño periódicamente durante la ejecución del programa, y ​​esto puede provocar un problema llamado fragmentación.
La fragmentación ocurre cuando a los objetos de memoria se les asignan pequeños espacios entre ellos que son demasiado pequeños para contener objetos de memoria adicionales.
El resultado neto es un porcentaje del espacio del montón que no se puede utilizar para asignaciones de memoria adicionales.

Ambos juntos

En una aplicación multiproceso, cada subproceso tendrá su propia pila.Pero todos los diferentes hilos compartirán el montón.
Debido a que los diferentes subprocesos comparten el montón en una aplicación de subprocesos múltiples, esto también significa que tiene que haber cierta coordinación entre los subprocesos para que no intenten acceder y manipular los mismos fragmentos de memoria en el montón en al mismo tiempo.

¿Qué es más rápido: la pila o el montón?¿Y por qué?

La pila es mucho más rápida que el montón.
Esto se debe a la forma en que se asigna la memoria en la pila.
Asignar memoria en la pila es tan simple como mover el puntero de la pila hacia arriba.

Para las personas nuevas en la programación, probablemente sea una buena idea utilizar la pila, ya que es más fácil.
Debido a que la pila es pequeña, querrás usarla cuando sepas exactamente cuánta memoria necesitarás para tus datos, o si sabes que el tamaño de tus datos es muy pequeño.
Es mejor usar el montón cuando sabes que necesitarás mucha memoria para tus datos, o simplemente no estás seguro de cuánta memoria necesitarás (como con una matriz dinámica).

Modelo de memoria Java

Enter image description here

La pila es el área de la memoria donde se almacenan las variables locales (incluidos los parámetros del método).Cuando se trata de variables de objetos, éstas son meras referencias (punteros) a los objetos reales en el montón.
Cada vez que se crea una instancia de un objeto, se reserva una porción de memoria dinámica para contener los datos (estado) de ese objeto.Dado que los objetos pueden contener otros objetos, algunos de estos datos pueden contener referencias a esos objetos anidados.

Puedes hacer algunas cosas interesantes con la pila.Por ejemplo, tienes funciones como asignar (suponiendo que pueda superar las numerosas advertencias sobre su uso), que es una forma de malloc que usa específicamente la pila, no el montón, para la memoria.

Dicho esto, los errores de memoria basados ​​en pila son algunos de los peores que he experimentado.Si usa memoria de montón y sobrepasa los límites de su bloque asignado, tiene una buena posibilidad de desencadenar una falla de segmento.(No 100%:su bloque puede ser incidentalmente contiguo a otro que haya asignado previamente). Pero dado que las variables creadas en la pila siempre son contiguas entre sí, escribir fuera de los límites puede cambiar el valor de otra variable.He aprendido que cada vez que siento que mi programa ha dejado de obedecer las leyes de la lógica, probablemente se trata de un desbordamiento del buffer.

Simplemente, la pila es donde se crean las variables locales.Además, cada vez que llama a una subrutina, el contador del programa (puntero a la siguiente instrucción de la máquina) y cualquier registro importante y, a veces, los parámetros se insertan en la pila.Luego, cualquier variable local dentro de la subrutina se coloca en la pila (y se usa desde allí).Cuando finaliza la subrutina, todo ese material se vuelve a sacar de la pila.La PC y los datos de registro se recuperan y se devuelven a donde estaban tal como fueron extraídos, para que su programa pueda continuar su camino alegremente.

El montón es el área de memoria a partir de la cual se realizan las asignaciones de memoria dinámica (llamadas explícitas "nuevas" o "asignadas").Es una estructura de datos especial que puede realizar un seguimiento de bloques de memoria de diferentes tamaños y su estado de asignación.

En los sistemas "clásicos", la RAM se disponía de manera que el puntero de la pila comenzaba en la parte inferior de la memoria, el puntero del montón comenzaba en la parte superior y crecían uno hacia el otro.Si se superponen, te quedas sin RAM.Sin embargo, eso no funciona con los sistemas operativos multiproceso modernos.Cada hilo debe tener su propia pila, y éstas pueden crearse dinámicamente.

De WikiAnwser.

Pila

Cuando una función o un método llama a otra función que a su vez llama a otra función, etc., la ejecución de todas esas funciones permanece suspendida hasta que la última función devuelve su valor.

Esta cadena de llamadas a funciones suspendidas es la pila, porque los elementos de la pila (llamadas a funciones) dependen unos de otros.

Es importante tener en cuenta la pila en el manejo de excepciones y la ejecución de subprocesos.

Montón

El montón es simplemente la memoria utilizada por los programas para almacenar variables.Los elementos del montón (variables) no tienen dependencias entre sí y siempre se puede acceder a ellos de forma aleatoria en cualquier momento.

Pila

  • Acceso muy rápido
  • No es necesario desasignar variables explícitamente
  • El espacio es administrado eficientemente por la CPU, la memoria no se fragmentará
  • Sólo variables locales
  • Límite de tamaño de pila (depende del sistema operativo)
  • No se puede cambiar el tamaño de las variables

Montón

  • Se puede acceder a las variables globalmente
  • Sin límite de tamaño de memoria
  • Acceso (relativamente) más lento
  • No se garantiza un uso eficiente del espacio; la memoria puede fragmentarse con el tiempo a medida que se asignan bloques de memoria y luego se liberan
  • Debes administrar la memoria (tú eres el encargado de asignar y liberar variables)
  • Las variables se pueden cambiar de tamaño usando realloc()

DE ACUERDO, de manera simple y breve, quieren decir ordenado y no ordenado...!

Pila:En los elementos apilados, las cosas se colocan una encima de la otra, ¡lo que significa que su procesamiento será más rápido y más eficiente!...

Así que siempre hay un índice para señalar el elemento específico, además el procesamiento será más rápido, ¡también hay una relación entre los elementos!...

Montón:Sin orden, el procesamiento será más lento y los valores estarán desordenados sin ningún orden o índice específico...son aleatorios y no hay relación entre ellos...por lo que el tiempo de ejecución y uso podría variar...

También creo la imagen a continuación para mostrar cómo se verían:

enter image description here

En breve

Se utiliza una pila para la asignación de memoria estática y un montón para la asignación de memoria dinámica, ambos almacenados en la RAM de la computadora.


En detalle

La pila

La pila es una estructura de datos "LIFO" (último en entrar, primero en salir), que la CPU gestiona y optimiza estrechamente.Cada vez que una función declara una nueva variable, se "empuja" a la pila.Luego, cada vez que sale una función, todas las variables empujadas a la pila por esa función se liberan (es decir, se eliminan).Una vez que se libera una variable de la pila, esa región de memoria queda disponible para otras variables de la pila.

La ventaja de utilizar la pila para almacenar variables es que la memoria se administra por usted.No es necesario asignar memoria manualmente ni liberarla cuando ya no la necesite.Es más, debido a que la CPU organiza la memoria de la pila de manera tan eficiente, leer y escribir variables de la pila es muy rápido.

Se puede encontrar más aquí.


el montón

El montón es una región de la memoria de su computadora que no se administra automáticamente y que la CPU no administra de manera tan estricta.Es una región de memoria que flota más libremente (y es más grande).Para asignar memoria en el montón, debe usar malloc() o calloc(), que son funciones integradas de C.Una vez que haya asignado memoria en el montón, será responsable de utilizar free() para desasignar esa memoria una vez que ya no la necesite.

Si no hace esto, su programa tendrá lo que se conoce como pérdida de memoria.Es decir, la memoria del montón seguirá reservada (y no estará disponible para otros procesos).Como veremos en la sección de depuración, existe una herramienta llamada Valgrind que puede ayudarle a detectar pérdidas de memoria.

A diferencia de la pila, el montón no tiene restricciones de tamaño variable (aparte de las limitaciones físicas obvias de su computadora).La memoria del montón es un poco más lenta para leer y escribir, porque hay que usar punteros para acceder a la memoria en el montón.Hablaremos de sugerencias en breve.

A diferencia de la pila, cualquier función puede acceder a las variables creadas en el montón, en cualquier parte de su programa.Las variables del montón tienen un alcance esencialmente global.

Se puede encontrar más aquí.


Las variables asignadas en la pila se almacenan directamente en la memoria y el acceso a esta memoria es muy rápido, y su asignación se gestiona cuando se compila el programa.Cuando una función o un método llama a otra función que a su vez llama a otra función, etc., la ejecución de todas esas funciones permanece suspendida hasta que la última función devuelve su valor.La pila siempre está reservada en orden LIFO, el bloque reservado más recientemente es siempre el siguiente bloque que se liberará.Esto hace que sea realmente sencillo realizar un seguimiento de la pila, liberar un bloque de la pila no es más que ajustar un puntero.

Las variables asignadas en el montón tienen su memoria asignada en tiempo de ejecución y el acceso a esta memoria es un poco más lento, pero el tamaño del montón solo está limitado por el tamaño de la memoria virtual.Los elementos del montón no tienen dependencias entre sí y siempre se puede acceder a ellos de forma aleatoria en cualquier momento.Puedes asignar un bloque en cualquier momento y liberarlo en cualquier momento.Esto hace que sea mucho más complejo realizar un seguimiento de qué partes del montón están asignadas o libres en un momento dado.

Enter image description here

Puede utilizar la pila si sabe exactamente cuántos datos necesita asignar antes del tiempo de compilación y si no es demasiado grande.Puede utilizar el montón si no sabe exactamente cuántos datos necesitará en tiempo de ejecución o si necesita asignar muchos datos.

En una situación de subprocesos múltiples, cada subproceso tendrá su propia pila completamente independiente, pero compartirán el montón.La pila es específica del subproceso y el montón es específico de la aplicación.Es importante tener en cuenta la pila en el manejo de excepciones y la ejecución de subprocesos.

Cada subproceso obtiene una pila, mientras que normalmente solo hay un montón para la aplicación (aunque no es raro tener varios montones para diferentes tipos de asignación).

Enter image description here

En tiempo de ejecución, si la aplicación necesita más montón, puede asignar memoria de la memoria libre y si la pila necesita memoria, puede asignar memoria de la memoria libre asignada para la aplicación.

Incluso se dan más detalles aquí y aquí.


Ahora ven a las respuestas de tu pregunta.

¿Hasta qué punto están controlados por el sistema operativo o el tiempo de ejecución del lenguaje?

El sistema operativo asigna la pila para cada subproceso a nivel del sistema cuando se crea el subproceso.Normalmente, el tiempo de ejecución del lenguaje llama al sistema operativo para asignar el montón para la aplicación.

Se puede encontrar más aquí.

¿Cuál es su alcance?

Ya dado en la cima.

"Puedes usar la pila si sabes exactamente cuántos datos necesitas asignar antes del tiempo de compilación, y si no es demasiado grande.Puede utilizar el montón si no sabe exactamente cuántos datos necesitará en tiempo de ejecución o si necesita asignar muchos datos".

Se puede encontrar más en aquí.

¿Qué determina el tamaño de cada uno de ellos?

El tamaño de la pila lo establece SO cuando se crea un hilo.El tamaño del montón se establece al iniciar la aplicación, pero puede crecer a medida que se necesita espacio (el asignador solicita más memoria del sistema operativo).

¿Qué hace que uno sea más rápido?

La asignación de la pila es mucho más rápida ya que todo lo que realmente hace es mover el puntero de la pila.Al utilizar grupos de memoria, puede obtener un rendimiento comparable con la asignación del montón, pero eso conlleva una ligera complejidad adicional y sus propios dolores de cabeza.

Además, pila vs.El montón no es sólo una consideración de rendimiento;también le dice mucho sobre la vida útil esperada de los objetos.

Los detalles se pueden encontrar en aquí.

En la década de 1980, UNIX se propagó como conejitos y las grandes empresas desarrollaron las suyas propias.Exxon tenía uno, al igual que docenas de marcas perdidas en la historia.La forma en que se disponía la memoria quedaba a discreción de los muchos implementadores.

Un programa C típico se presentó en la memoria con la oportunidad de aumentar cambiando el valor BRK ().Por lo general, el montón estaba justo por debajo de este valor de BRK y el aumento de BRK aumentó la cantidad de montón disponible.

La sola pila era típicamente un área debajo del montón que era un tramo de memoria que no contenía nada de valor hasta la parte superior del siguiente bloque de memoria fijo.Este siguiente bloque era a menudo un código que podía ser sobrescribido por los datos de la pila en uno de los famosos hacks de su época.

Un bloque de memoria típico fue BSS (un bloque de valores cero) que accidentalmente no se cero en la oferta de un fabricante.Otro era DATOS que contenían valores inicializados, incluidas cadenas y números.Un tercero era CÓDIGO que contenía CRT (tiempo de ejecución de C), funciones principales y bibliotecas.

La llegada de la memoria virtual en UNIX cambia muchas de las limitaciones.No hay ninguna razón objetiva por la cual estos bloques deben ser contiguos, fijos en tamaño, o ordenar una manera particular ahora.Por supuesto, antes de UNIX eran Multics los que no padecían estas limitaciones.Aquí hay un esquema que muestra uno de los diseños de memoria de esa época.

A typical 1980s style UNIX C program memory layout

pila, montón y datos de cada proceso en la memoria virtual:

stack, heap and static data

Un par de centavos:Creo que sería bueno dibujar la memoria de forma gráfica y más sencilla:

This is my vision of process memory construction with simplification for more easy understanding wht happening


Flechas: muestran dónde crecen la pila y el montón, el tamaño de la pila del proceso tiene un límite, definido en el sistema operativo, los límites del tamaño de la pila de subprocesos generalmente se establecen mediante parámetros en la API de creación de subprocesos.El montón suele limitar el tamaño máximo de memoria virtual por proceso, por ejemplo, para 32 bits, entre 2 y 4 GB.

Manera tan sencilla:El montón de procesos es general para el proceso y todos los subprocesos internos, y se utiliza para la asignación de memoria en un caso común con algo como malloc().

La pila es una memoria rápida para almacenar variables y punteros de retorno de funciones de casos comunes, procesados ​​como parámetros en llamadas a funciones, variables de funciones locales.

Dado que algunas respuestas fueron quisquillosas, voy a contribuir con mi granito de arena.

Sorprendentemente, nadie ha mencionado que múltiples (es decir,(no relacionadas con el número de subprocesos en ejecución a nivel de sistema operativo) las pilas de llamadas se encuentran no sólo en lenguajes exóticos (PostScript) o plataformas (Intel Itanium), sino también en fibras, hilos verdes y algunas implementaciones de corrutinas.

Las fibras, los hilos verdes y las corrutinas son similares en muchos aspectos, lo que genera mucha confusión.La diferencia entre fibras e hilos verdes es que las primeras utilizan una multitarea cooperativa, mientras que los segundos pueden presentar una multitarea cooperativa o preventiva (o incluso ambas).Para conocer la distinción entre fibras y corrutinas, consulte aquí.

En cualquier caso, el propósito de ambas fibras, hilos verdes y corrutinas es tener múltiples funciones ejecutándose simultáneamente, pero no en paralelo (ver esta pregunta SO para la distinción) dentro de un único subproceso a nivel de sistema operativo, transfiriendo el control entre sí de forma organizada.

Al utilizar fibras, hilos verdes o corrutinas, usted generalmente tener una pila separada por función.(Técnicamente, no solo una pila sino todo un contexto de ejecución por función.Lo más importante son los registros de la CPU). Para cada subproceso hay tantas pilas como funciones se ejecutan simultáneamente, y el subproceso cambia entre la ejecución de cada función de acuerdo con la lógica de su programa.Cuando una función llega al final, su pila se destruye.Entonces, el número y la vida útil de las pilas son dinámicos y ¡No están determinados por la cantidad de subprocesos a nivel del sistema operativo!

Tenga en cuenta que dije "generalmente tener una pila separada por función".hay ambos apilado y sin pila Implementaciones de rutinas.Las implementaciones de C++ apilables más notables son Impulso.Corrutina y PPL de Microsoft's async/await.(Sin embargo, C++ funciones reanudables (también conocido como"async y await"), que se propusieron para C++ 17, probablemente utilicen corrutinas sin pila).

La propuesta de Fibers para la biblioteca estándar de C++ está próxima a publicarse.Además, hay algunos terceros bibliotecas.Los hilos verdes son extremadamente populares en lenguajes como Python y Ruby.

Tengo algo que compartir, aunque los puntos principales ya están cubiertos.

Pila

  • Acceso muy rápido.
  • Almacenado en RAM.
  • Las llamadas a funciones se cargan aquí junto con las variables locales y los parámetros de función pasados.
  • El espacio se libera automáticamente cuando el programa sale de su alcance.
  • Almacenado en memoria secuencial.

Montón

  • Acceso lento comparativamente a Stack.
  • Almacenado en RAM.
  • Las variables creadas dinámicamente se almacenan aquí, lo que luego requiere liberar la memoria asignada después de su uso.
  • Almacenado dondequiera que se realice la asignación de memoria, siempre se accede mediante un puntero.

Nota interesante:

  • Si las llamadas a funciones se hubieran almacenado en el montón, habrían resultado en 2 puntos complicados:
    1. Debido al almacenamiento secuencial en la pila, la ejecución es más rápida.El almacenamiento en el montón habría supuesto un gran consumo de tiempo, lo que haría que todo el programa se ejecutara más lentamente.
    2. Si las funciones se almacenaran en el montón (almacenamiento desordenado señalado por un puntero), no habría habido forma de regresar a la dirección de la persona que llama (lo que la pila proporciona debido al almacenamiento secuencial en la memoria).

Muchas respuestas son correctas como conceptos, pero debemos tener en cuenta que el hardware necesita una pila (es decir,microprocesador) para permitir la llamada a subrutinas (CALL en lenguaje ensamblador...).(Los chicos de OOP lo llamarán métodos)

En la pila, guarda las direcciones de retorno y la llamada → push / ret → pop se administra directamente en el hardware.

Puede utilizar la pila para pasar parámetros.incluso si es más lento que usar registros (diría un gurú de los microprocesadores o un buen libro de BIOS de los años 80...)

  • Sin pila No El microprocesador puede funcionar.(No podemos imaginar un programa, ni siquiera en lenguaje ensamblador, sin subrutinas/funciones)
  • Sin el montón puede hacerlo.(Un programa en lenguaje ensamblador puede funcionar sin él, ya que el montón es un concepto del sistema operativo, como malloc, que es una llamada OS/Lib.

El uso de la pila es más rápido como:

  • Es hardware, e incluso push/pop son muy eficientes.
  • malloc requiere ingresar al modo kernel, usar bloqueo/semáforo (u otras primitivas de sincronización) ejecutar algún código y administrar algunas estructuras necesarias para realizar un seguimiento de la asignación.

¡Guau!Tantas respuestas y no creo que ninguna haya acertado...

1) ¿Dónde y qué están (físicamente en la memoria de una computadora real)?

La pila es memoria que comienza como la dirección de memoria más alta asignada a la imagen de su programa y luego disminuye su valor a partir de ahí.Está reservado para los parámetros de la función llamada y para todas las variables temporales utilizadas en las funciones.

Hay dos montones:pública y privada.

El montón privado comienza en un límite de 16 bytes (para programas de 64 bits) o un límite de 8 bytes (para programas de 32 bits) después del último byte de código de su programa y luego aumenta su valor a partir de ahí.También se le llama montón predeterminado.

Si el montón privado crece demasiado, se superpondrá al área de la pila, al igual que la pila se superpondrá al montón si crece demasiado.Debido a que la pila comienza en una dirección más alta y avanza hasta una dirección más baja, con la piratería adecuada puede hacer que la pila sea tan grande que invadirá el área privada del montón y se superpondrá al área de código.Entonces, el truco consiste en superponer una cantidad suficiente del área del código para que pueda conectarse al código.Es un poco complicado de hacer y corre el riesgo de que el programa se bloquee, pero es fácil y muy efectivo.

El montón público reside en su propio espacio de memoria fuera del espacio de imagen de su programa.Es esta memoria la que se desviará al disco duro si los recursos de memoria escasean.

2) ¿Hasta qué punto están controlados por el sistema operativo o el tiempo de ejecución del lenguaje?

El programador controla la pila, el sistema operativo administra el montón privado y nadie controla el montón público porque es un servicio del sistema operativo: usted realiza solicitudes y se conceden o rechazan.

2b) ¿Cuál es su alcance?

Todos son globales para el programa, pero su contenido puede ser privado, público o global.

2c) ¿Qué determina el tamaño de cada uno de ellos?

El tamaño de la pila y del montón privado están determinados por las opciones de tiempo de ejecución del compilador.El montón público se inicializa en tiempo de ejecución mediante un parámetro de tamaño.

2d) ¿Qué hace que uno sea más rápido?

No están diseñados para ser rápidos, están diseñados para ser útiles.La forma en que el programador los utiliza determina si son "rápidos" o "lentos".

ÁRBITRO:

https://norasandler.com/2019/02/18/Write-a-Compiler-10.html

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top