25 Pregunta: ¿Qué y dónde están la pila y el montón?

pregunta creada en Wed, Apr 10, 2019 12:00 AM
Los

libros de lenguaje de programación explican que los tipos de valor se crean en la 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 es una pila . 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 idioma en tiempo de ejecución?
  • ¿Cuál es su alcance?
  • ¿Qué determina el tamaño de cada uno de ellos?
  • ¿Qué hace que uno sea más rápido?
7663
  1. aquí se puede encontrar una muy buena explicación ¿Cuál es la diferencia entre una pila y un montón?
    2013-12-16 11: 32: 49Z
  2. También (realmente) bueno: codeproject.com/Articles/76153/… (la parte de pila /montón)
    2014-02-15 05: 50: 34Z
  3. 2016-06-11 05: 42: 54Z
  4. Relacionados, vea Stack Clash . Las soluciones de Stack Clash afectaron algunos aspectos de las variables y comportamientos del sistema como rlimit_stack. También vea Red Hat Issue 1463241
    2017-06-21 16: 23: 19Z
  5. @ mattshane Las definiciones de stack y heap no dependen del valor ni de los tipos de referencia en absoluto. En otras palabras, la pila y el montón se pueden definir completamente incluso si los tipos de valor y referencia nunca existieron. Además, cuando se comprenden los tipos de valor y referencia, la pila es solo un detalle de implementación. Según Eric Lippert: La pila es un detalle de implementación, Parte Uno .
    2017-11-12 22: 38: 03Z
25 Respuestas                              25                         

La pila es la memoria reservada como espacio de memoria virtual para un subproceso 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 de contabilidad. 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 se reserva en un 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 hacer un seguimiento de la pila; liberar un bloque de la pila no es más que ajustar un puntero.

El montón es memoria asignada para la asignación dinámica. A diferencia de la pila, no hay un patrón forzado para la asignación y desasignación de bloques del montón; Puede asignar un bloque en cualquier momento y liberarlo en cualquier momento. Esto hace que sea mucho más complejo hacer un seguimiento de qué partes del montón se asignan o liberan en un momento dado; hay muchos asignadores de pila personalizados disponibles para ajustar el rendimiento de pila 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 de idioma?

El sistema operativo asigna la pila para cada subproceso a nivel del sistema cuando se crea el subproceso. Tipicamente tEl sistema de ejecución de idioma llama al sistema operativo para asignar el montón para la aplicación.

  

¿Cuál es su alcance?

La pila está unida a un hilo, por lo que cuando el hilo sale de la pila se reclama. El montón generalmente se asigna al inicio de la aplicación por el tiempo de ejecución, y se reclama cuando la aplicación (proceso técnico) 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 en el inicio de la aplicación, pero puede aumentar 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 en 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 problema de rendimiento para el montón es que el montón, que es principalmente un recurso global, generalmente tiene que ser seguro para múltiples subprocesos, es decir, cada asignación y desasignación debe estar, por lo general, sincronizada con "todos" los demás accesos del montón en el programa. /p>

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

    
5652
2016-05-02 12: 35: 48Z
  1. Buena respuesta, pero creo que debería agregar que mientras el sistema operativo asigna la pila cuando se inicia el proceso (asumiendo la existencia de un sistema operativo), se mantiene en línea por el programa. Esta es otra razón por la que la pila es más rápida, ya que las operaciones de inserción y pop son generalmente una instrucción de máquina, y las máquinas modernas pueden hacer al menos 3 de ellas en un ciclo, mientras que asignar o liberar el montón implica llamar al código del sistema operativo.
    2013-10-08 08: 31: 45Z
  2. Estoy realmente confundido por el diagrama al final. Pensé que lo tenía hasta que vi esa imagen.
    2016-08-15 19: 06: 04Z
  3. @ Anarelle el procesador ejecuta instrucciones con o sin un sistema operativo. Un ejemplo cercano a mi corazón es el SNES, que no tenía llamadas de API, ningún sistema operativo como lo conocemos hoy en día, pero tenía una pila. Asignar en una pila es la suma y la resta en estos sistemas y eso está bien para las variables destruidas cuando se extraen al regresar de la función que las creó, pero contradice eso a, digamos, un constructor, cuyo resultado no puede ser simplemente tirado Para eso necesitamos el montón, que no está atado a llamar y devolver. La mayoría de los sistemas operativos tienen un montón de API, no hay razón para hacerlo por tu cuenta
    2016-10-13 15: 06: 55Z
  4. "stack es la memoria reservada como espacio de memoria". Guay. Pero, ¿dónde está realmente "apartado" en términos de la estructura de memoria de Java? Es memoria de almacenamiento dinámico /Memoria no de almacenamiento dinámico /Otro (estructura de memoria Java según betsol.com/2017/06/… )
    2018-07-22 06: 22: 38Z
  5. @ JatinShashoo Java runtime, como intérprete de bytecode, agrega un nivel más de virtualización, por lo que se refiere al punto de vista de la aplicación Java. Desde el punto de vista del sistema operativo, todo eso es solo un montón, donde el proceso de ejecución de Java asigna parte de su espacio como memoria "no de montón" para el código de bytes procesado. El resto de ese montón de nivel de SO se usa como montón de nivel de aplicación, donde se almacenan los datos del objeto.
    2018-09-06 15: 41: 48Z

Stack:

  • Almacenado en la memoria RAM de la computadora al igual que el montón.
  • Las variables creadas en la pila quedarán fuera del alcance y se eliminarán automáticamente.ed.
  • Es 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, utilizadas para pasar parámetros.
  • Puede tener un desbordamiento de pila cuando se usa demasiado de la pila (principalmente de recursión infinita o demasiado profunda, asignaciones muy grandes).
  • Los datos creados en la pila se pueden usar sin punteros.
  • Usaría la pila si sabe exactamente cuántos datos necesita asignar antes del tiempo de compilación y no es demasiado grande.
  • Por lo general, el tamaño máximo ya está determinado cuando se inicia el programa.

Heap:

  • Almacenado en la memoria 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 en la pila.
  • Se utiliza a pedido para asignar un bloque de datos para uso del programa.
  • Puede tener fragmentación cuando hay muchas asignaciones y desasignaciones.
  • En C ++ o C, los datos creados en el montón serán señalados por punteros y asignados con new o malloc respectivamente.
  • Puede tener errores de asignación si se solicita un búfer demasiado grande para ser asignado.
  • Usaría el montón si no sabe exactamente cuántos datos necesitará en el tiempo de ejecución o si necesita asignar una gran cantidad de datos.
  • Responsable de las fugas 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;
    
2237
2017-07-28 00: 38: 33Z
  1. El puntero pBuffer y el valor de b están ubicados en la pila, y en su mayoría están asignados a la entrada de la función. Dependiendo del compilador, el búfer también puede asignarse en la entrada de la función.
    2009-03-18 22: 48: 52Z
  2. Es un error común que el lenguaje C, según lo define el estándar de lenguaje C99 (disponible en open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf ), requiere un "apilar". De hecho, la palabra 'pila' ni siquiera aparece en el estándar. Esto responde a las afirmaciones de wrt /to C: el uso de la pila es cierto en general, pero el idioma no lo exige de ninguna manera. Consulte knosof.co.uk/cbook/cbook.html para obtener más información, y, en particular, cómo se implementa C en arquitecturas extrañas como en.wikipedia.org/wiki/Burroughs_large_systems
    2009-09-01 04: 37: 30Z
  3. @ Brian Debes explicar por qué el búfer [] y el puntero pBuffer se crean en la pila y por qué los datos de pBuffer se crean en el montón. Creo que su respuesta podría confundir a algunos ppl, ya que podrían pensar que el programa indica específicamente que la memoria se asigne en la pila frente al montón, pero este no es el caso. ¿Es porque Buffer es un tipo de valor mientras que pBuffer es un tipo de referencia?
    2010-02-08 04: 56: 47Z
  4. @ Remover: no hay un puntero que contenga una dirección y puede apuntar a algo en el montón o la pila por igual. new, malloc y algunas otras funciones similares a malloc se asignan en el montón y devuelven la dirección de la memoria que se asignó. ¿Por qué querrías asignar en el montón? Para que su memoria no quede fuera del alcance y se libere hasta que usted lo desee.
    2012-05-26 23: 25: 58Z
  5. "Responsable de las pérdidas de memoria" - ¡Los montones no son responsables de las pérdidas de memoria! ¡Los codificadores /codificadores perezosos /olvidados /ex-java que no dan una mierda son!
    2013-03-25 08: 22: 06Z

El punto más importante es que pila y pila son términos genéricos para 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 artículos, los objetos se colocan uno encima del otro en el orden en que se colocaron allí, y solo puedes quitar el superior (sin derribar todo el objeto).

    Se apilan como una pila de papeles

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

  • En un montón, no hay 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.

    Montón como un montón de surtidos de regaliz

    La asignación de montones requiere mantener un registro completo de qué memoria está asignada y qué no, así como un mantenimiento general 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 realiza tareas de mantenimiento, como desfragmentar la memoria, moviendo la memoria asignada o la recolección de basura, identificándose en el tiempo de ejecución cuando la memoria ya no está en el alcance y desasignándola.

Estas imágenes deberían hacer un buen trabajo al describir las dos formas de asignar y liberar memoria en una pila y un montón. Yum!

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

    Como se mencionó, heap y stack son términos generales, y pueden implementarse 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 una 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 encoge para mantener la información de las funciones más abajo en la pila de llamadas. Un programa no tiene realmente control sobre el tiempo de ejecución; 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 orden. La memoria normalmente es asignada por el sistema operativo, y la aplicación llama a las funciones de la API para realizar esta asignación. Hay un poco de sobrecarga requerida 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 nivel tan bajo que no se relaciona con 'alcance' en el sentido de programación. Si desmonta algún código, verá 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. Un aspecto importante de una pila, sin embargo, es que una vez que una función regresa, cualquier cosa local a esa función se libera inmediatamente de la pila. Eso funciona de la manera que usted esperaría, dado el funcionamiento de sus lenguajes de programación. En un montón, también es difícil de definir. El alcance es lo que sea expuesto por 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 se traduce en direcciones físicas y hay fallas de página, etc. Mantienen un registro de qué páginas pertenecen a qué aplicaciones. Sin embargo, nunca debe preocuparse realmente por esto, ya que solo utiliza el método que utiliza su lenguaje de programación para asignar y liberar memoria, y verifica si hay errores (si la asignación /liberación falla por algún motivo).

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

    Una vez más, depende del idioma, el compilador, el sistema operativo y la arquitectura. Por lo general, una pila está asignada previamente, porque, por definición, debe ser una memoria contigua (más sobre esto en el último párrafo). El compilador de idiomas o el sistema operativo determinan su tamaño. No almacena grandes cantidades de datos en la pila, por lo que será lo suficientemente grande como para que nunca se use por completo, excepto en casos de recursión infinita no deseada (por lo tanto, "desbordamiento de pila") u otras decisiones de programación inusuales. /p>

    Un montón es un término general para cualquier cosa que pueda asignarse dinámicamente. Dependiendo de cómo lo mires, cambia de tamaño constantemente. De todos modos, en los procesadores y sistemas operativos modernos, la forma exacta en que funciona es muy abstracta, por lo que normalmente no tiene que preocuparse mucho por cómo funciona en el fondo, excepto que (en los idiomas en que lo permite) no debe usar memoria que no tienes alSe encuentra aún o la memoria que ha 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 generalmente almacenan este puntero en un registro rápido y especial para este propósito. Además, las operaciones posteriores en una pila generalmente se concentran en áreas muy cercanas de la memoria, lo que a un nivel muy bajo es bueno para la optimización por parte de las memorias caché del procesador.

1323
2017-12-17 22: 26: 52Z
  1. David No estoy de acuerdo con que sea una buena imagen o que "stack down-down" sea un buen término para ilustrar el concepto. Cuando agrega algo a una pila, los otros contenidos de la pila no se presionan hacia abajo, permanecen donde están.
    2012-08-13 03: 40: 14Z
  2. Esta respuesta incluye un gran error. Las variables estáticas no se asignan en la pila. Consulte mi respuesta [enlace] stackoverflow.com/a/13326916/1763801 para obtener una aclaración. está equiparando las variables "automáticas" con las variables "estáticas", pero no son en absoluto las mismas
    2012-11-10 23: 07: 01Z
  3. Específicamente, usted dice que las "variables locales asignadas estáticamente" están asignadas en la pila. En realidad se asignan en el segmento de datos. Solo las variables asignadas automáticamente (que incluyen la mayoría pero no todas las variables locales y también cosas como parámetros de función pasados ​​por valor en lugar de por referencia) se asignan en la pila.
    2012-11-11 01: 44: 52Z
  4. Me acabo de dar cuenta de que tienes razón: en C, la asignación estática es su propia cosa en lugar de un término para cualquier cosa que no sea dinámico . He editado mi respuesta, gracias.
    2012-11-12 00: 29: 15Z
  5. No es solo C. Java, Pascal, Python y muchos otros tienen las nociones de asignación estática versus asignación automática versus dinámica. Decir "asignación estática" significa lo mismo en casi todas partes. En ningún idioma, la asignación estática significa "no dinámico". Desea el término asignación "automática" para lo que está describiendo (es decir, las cosas en la pila).
    2012-11-12 17: 16: 28Z

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

La respuesta a su pregunta es específica de la implementación y puede variar entre 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, la memoria virtual que se asigna a la memoria física a pedido).
  • 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 libres. Las nuevas asignaciones en el montón (por new o malloc) se satisfacen creando un bloque adecuado de uno de los bloques libres. Esto requiere actualizar la lista de bloques en el montón. Esta metainformació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 crece el montón, a menudo se asignan bloques desde direcciones inferiores a direcciones superiores. Por lo tanto, puede pensar en el montón como un montón de bloques de memoria que crece en tamaño a medida que se asigna la memoria. Si el montón es demasiado pequeño para una asignación, el tamaño a menudo se puede aumentar al adquirir 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 una gran cantidad de pequeños bloques libres intercalados entre los bloques utilizados. Una solicitud para asignar un bloque grande puedefalla porque ninguno de los bloques libres es lo suficientemente grande como para satisfacer la solicitud de asignación, aunque el tamaño combinado de los bloques libres sea lo suficientemente grande. Esto se denomina fragmentación del montón .
  • Cuando un bloque usado que es adyacente a un bloque libre se desasigna, el nuevo bloque libre se puede fusionar con el bloque libre adyacente para crear un bloque libre más grande que reduce efectivamente la fragmentación del montón.

The heap

La pila

  • La pila a menudo funciona en tándem con un registro especial en la CPU llamada apilador de pila . Inicialmente, el puntero de la pila apunta a la parte superior de la pila (la dirección más alta en la pila).
  • La CPU tiene instrucciones especiales para empujar los valores en la pila y sacarlos de la pila. Cada push almacena el valor en la ubicación actual del puntero de pila y disminuye el puntero de pila. Un pop recupera el valor apuntado por el puntero de pila y luego aumenta el puntero de pila (no se confunda por el hecho de que agrega un valor a la pila disminuye el puntero de la pila y eliminando un valor lo incrementa . 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 usa instrucciones especiales que empujan el puntero de instrucción actual, es decir, la dirección del código que se ejecuta en la pila. La CPU luego salta a la función configurando Indicador de instrucción a 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 pila se reduce para asignar más espacio en la pila para las 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 insertan en la pila antes de la llamada a la función. El código de la función puede navegar hacia arriba en la pila desde el puntero de la pila actual para ubicar estos valores.
  • Las llamadas a funciones de anidamiento funcionan como un encanto. Cada nueva llamada asignará los parámetros de la función, la dirección de retorno y el espacio para las variables locales y estos registros de activación se pueden apilar para llamadas anidadas y se desenrollarán de la manera correcta cuando las funciones regresen.
  • 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 las 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 provocará una captura o excepción en la CPU. Esta condición excepcional puede ser capturada por el tiempo de ejecución y convertida 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 una pila?

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

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

Sin embargo, la pila es una característica de más bajo nivel estrechamente vinculada 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 de la biblioteca que maneja el montón. Sin embargo, a menudo es imposible hacer crecer la pila, ya que el desbordamiento de pila solo se descubre cuando es demasiado tarde; y cerrar la cadena de ejecución es la única opción viable.

    
695
2012-03-28 23: 03: 30Z
  1. @ Martin - Una respuesta /explicación muy buena que la respuesta más abstracta aceptada. Un programa de ensamblaje de muestra que muestre los punteros /registros de pila que se utilizan para las llamadas a la función sería más ilustrativo.
    2012-04-25 16: 42: 17Z
  2. Cada tipo de referencia es una composición de tipos de valor (int, cadena, etc.). Como se dice, los tipos de valor se almacenan en la pila de cómo funciona cuando son parte del tipo de referencia.
    2014-02-15 10: 26: 30Z
  3. Esta fue la mejor respuesta en mi opinión, porque me ayudó a entender qué es realmente una declaración de devolución y cómo se relaciona con esta "dirección de respuesta" con la que me encuentro. de vez en cuando, qué significa empujar una función en la pila y por qué las funciones se empujan en las pilas. ¡Gran respuesta!
    2014-03-19 19: 59: 21Z
  4. Este es el mejor en mi opinión, especialmente por mencionar que el montón /pila son muy específicos de implementación. Las otras respuestas asumen un lote de cosas sobre el idioma y el entorno /SO. +1
    2014-11-29 02: 00: 15Z
  5. ¿Qué quiere decir con "El código en la función puede entonces navegar hacia la pila desde el puntero de la pila actual para ubicar estos valores". ? ¿Puedes explicar esto por favor?
    2015-05-03 09: 36: 25Z

En el siguiente código C #

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

Así es como se gestiona la memoria

Imagen de variables en la pila

Local Variables que solo debe durar mientras la invocación de la función vaya en la pila. El montón se utiliza para variables cuya vida útil no sabemos realmente al principio, pero esperamos que duren un tiempo. En la mayoría de los idiomas 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 según los actualizamos) se acumulan porque no sabemos en el momento de la creación cuánto durarán. En muchos idiomas, el montón se recolecta como basura para encontrar objetos (como el objeto cls1) que ya no tienen ninguna referencia.

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

Puede encontrar más información aquí:

La diferencia entre stack y asignación de memoria del montón «timmurphy.org

y aquí:

Crear objetos en la pila y el montón

Este artículo es la fuente de la imagen anterior: Seis conceptos importantes de .NET: Stack, Heap, tipos de valores, tipos de referencia, boxeo y unboxing - CodeProject

pero ten en cuenta que puede contener algunas inexactitudes.

    
388
2018-03-07 10: 27: 28Z
  1. Esto es incorrecto. i y cls no son variables "estáticas". Se les llama variables "locales" o "automáticas". Es una distinción muy importante. Consulte [link] stackoverflow.com/a/13326916/1763801 para aclaraciones
    2012-11-10 23: 05: 05Z
  2. No dije que eran variables estáticas . Dije que int y cls1 son elementos estáticos . Su memoria está asignada estáticamente y por lo tanto van en la pila. Esto contrasta con un objeto que requiere una asignación de memoria dinámica que, por lo tanto, va en el montón.
    2012-11-20 14: 38: 13Z
  3. Cito "Elementos estáticos ... ir a la pila". Esto es simplemente un error. Los elementos estáticos van en el segmento de datos, los elementos automáticos van en la pila.
    2012-11-21 16: 55: 41Z
  4. Además, quien haya escrito el artículo de codeproject no sabe de qué está hablando. Por ejemplo, dice que "los primitivos necesitan memoria de tipo estático", lo cual es completamente falso. Nada le impide asignar primitivas en el montón dinámicamente, solo escriba algo como "int array [] = newint [num] "y voila, primitivas asignadas dinámicamente en .NET. Esa es solo una de varias inexactitudes.
    2012-11-21 17: 02: 46Z
  5. Edité tu publicación porque has cometido graves errores técnicos sobre lo que va en la pila y el montón.
    2014-09-09 23: 09: 12Z

La pila Cuando llama a una función, los argumentos de esa función más algunos gastos generales se colocan en la pila. Algunos datos (como dónde ir en la devolución) también se almacenan allí. Cuando declara una variable dentro de su función, esa variable también se asigna en la pila.

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

El montón El montón es un nombre genérico para colocar los datos que crea sobre la marcha. Si no sabes cuántas naves espaciales creará tu programa, es probable que uses el operador nuevo (o malloc o equivalente) para crear cada nave espacial. Esta asignación se mantendrá durante un tiempo, por lo que es probable que liberemos las cosas en un orden diferente al que las creamos.

Por lo tanto, el montón es mucho más complejo, porque terminan siendo regiones de memoria que no se utilizan intercaladas con fragmentos que están: la memoria se fragmenta. Encontrar la memoria libre del tamaño que necesita es un problema difícil. Esta es la razón por la que el montón debe evitarse (aunque todavía se usa a menudo).

Implementación La 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 tienen un rendimiento crítico 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 solo es práctico si el uso de la memoria es muy diferente de la norma, es decir, para juegos en los que se carga un nivel en una operación enorme y puede eliminar todo el lote en otra operación enorme.

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

    
197
2008-09-17 04: 34: 33Z
  1. Una recomendación para evitar el uso del montón es bastante fuerte. Los sistemas modernos tienen buenos administradores de pilas, y los lenguajes dinámicos modernos usan la pila de manera extensa (sin que el programador se preocupe realmente por eso). Yo diría que use el montón, pero con un asignador manual, ¡no se olvide de liberar!
    2008-09-17 04: 31: 42Z
  2. Si puedes usar la pila o el montón, usa la pila. Si no puedes usar la pila, realmente no hay opción. Uso mucho a la vez y, por supuesto, utilizando std :: vector o similares para el montón. ¡Para un principiante, evita el montón porque la pila es muy fácil!
    2008-09-17 04: 35: 51Z
  3. Si su idioma no implementa la recolección de basura, los punteros inteligentes (los objetos asignados de manera esporádica que rodean un puntero que hace el recuento de referencias para los fragmentos de memoria asignados dinámicamente) están estrechamente relacionados a la recolección de basura y son una forma decente de administrar el montón de manera segura y sin fugas. Se implementan en varios marcos, pero tampoco son tan difíciles de implementar para sus propios programas.
    2016-10-11 19: 10: 40Z
  4. "Esta es la razón por la que el montón debe ser evitadod (aunque todavía se usa con frecuencia). "No estoy seguro de lo que esto significa en la práctica, especialmente porque la memoria se maneja de manera diferente en muchos lenguajes de alto nivel. Como esta pregunta está etiquetada como agnóstica del lenguaje, diría este comentario en particular /la línea está mal colocada y no es aplicable.
    2018-07-25 09: 34: 14Z
  5. Buen punto @JonnoHampson - Mientras hace un punto válido, argumentaría que si está trabajando en un "lenguaje de alto nivel" con un GC, probablemente no lo haga. No se preocupa en absoluto por los mecanismos de asignación de memoria, por lo que no importa lo que sean la pila y el montón.
    2018-07-26 22: 22: 52Z

Para aclarar, esta respuesta tiene información incorrecta ( thomas corrigió su respuesta después de los comentarios, genial :)). Otras respuestas simplemente evitan explicar qué significa la asignación estática. Por lo tanto, 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 a continuación. También mostraré algunos ejemplos tanto en C /C ++ como en Python para ayudar a las personas a entender.

Las variables "Estáticas" (AKA asignadas estáticamente) no se asignan en la pila. No lo asuma, muchas personas lo hacen solo porque "estática" se parece mucho a "apilar". Realmente no existen ni en la pila ni en el montón. Son parte de lo que se llama el segmento de datos .

Sin embargo, generalmente es mejor considerar " alcance " y " vida útil " en lugar de "pila" y "montón".

El alcance se refiere a qué partes del código pueden acceder a una variable. En general, pensamos en ámbito local (solo puede accederse a través de la función actual) frente a ámbito global (se puede acceder desde cualquier parte), aunque el ámbito puede ser mucho más complejo.

Vida útil se refiere a cuando una variable se asigna y se desasigna durante la ejecución del programa. Por lo general, pensamos en asignación estática (la variable se mantendrá durante toda la duración del programa, por lo que es útil para almacenar la misma información en varias llamadas a funciones) en lugar de asignación automática (variable solo persiste durante una única llamada a una función, por lo que es útil para almacenar información que solo se usa durante su función y se puede descartar una vez que haya terminado) en lugar de asignación dinámica (variables cuya duración se define en tiempo de ejecución , en lugar de compilar el tiempo como estático o automático).

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 lo desea, siempre que el comportamiento sea correcto. Por ejemplo, debido a la optimización, una variable local solo puede existir en un registro o eliminarse por completo, aunque la mayoría de las variables locales existen en la pila. Como se ha señalado en algunos comentarios, tiene la libertad de implementar un compilador que ni siquiera utiliza una pila o un montón, sino otros mecanismos de almacenamiento (rara vez se hace, ya que las pilas y los montones son excelentes para esto). /p>

Proporcionaré un código C anotado simple para ilustrar todo esto. La mejor manera de aprender es ejecutar un programa bajo un depurador y observar el comportamiento. Si prefieres leer python, salta al 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 la duración y el alcance es que una variable puede tener un alcance local pero una duración estática, por ejemplo, "someLocalStaticVariable" en el ejemplo de código anterior. Tales variables pueden hacer que nuestros hábitos de nombramiento comunes pero informales sean muy confusos. Por ejemplo, cuando decimos " local " por lo general queremos decir " variable asignada automáticamente de ámbito local " y cuando decimos global, generalmente nos referimos a " variable asignada estáticamente con alcance global ". Desafortunadamente, cuando se trata de cosas como " archivo con variables asignadas estáticamente " muchas personas solo dicen ... " ¿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 colocar la palabra clave "estática" en la declaración anterior evita que var2 tenga alcance global. Sin embargo, la var1 global tiene asignación estática. ¡Esto no es intuitivo! Por esta razón, trato de nunca usar la palabra "estática" cuando describo el alcance, y en su lugar digo algo como "archivo" o "limitado por archivo". Sin embargo muchas personas nose la frase "estática" 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 útil, "estática" 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 ejemplo de Python a continuación ilustra los tres tipos de asignación (hay algunas diferencias sutiles posibles en los idiomas 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
    
178
2017-07-30 12: 13: 36Z
  1. Me referiría a una variable estática declarada dentro de una función que tiene solo accesibilidad local, pero generalmente no usaría el término "alcance" con eso. Además, puede valer la pena señalar que el aspecto de una pila /montón con el que los lenguajes tienen esencialmente flexibilidad cero: un lenguaje que guarda el contexto de ejecución en una pila no puede usar esa misma pila para contener cosas que necesitarán sobrevivir a los contextos en los que se crean . Algunos idiomas como PostScript tienen varias pilas, pero tienen un "montón" que se comporta más como una pila.
    2013-12-09 21: 53: 25Z
  2. @ supercat Eso tiene sentido. Definí el alcance como "qué partes del código pueden acceder a una variable" (y creo que esta es la definición más estándar), así que creo que estamos de acuerdo :)
    2013-12-17 20: 28: 57Z
  3. Consideraría que el "alcance" de una variable está limitado por el tiempo y el espacio. Se requiere una variable en el ámbito de clase-objeto para mantener su valor mientras exista el objeto. Se requiere una variable dentro de un ámbito de contexto de ejecución para mantener su valor mientras la ejecución permanezca en ese contexto. Una declaración de variable estática crea un identificador cuyo alcance está limitado al bloque actual, que se adjunta a una variable cuyo alcance no tiene límites.
    2013-12-17 20: 57: 52Z
  4. @ supercat Esta es la razón por la que utilizo la vida útil de la palabra, que es la forma en que denomino lo que se denomina alcance temporal. Reduce la necesidad de sobrecargar la palabra "alcance" con tantos significados. Por lo que puedo decir, no parece haber un consenso total sobre las definiciones exactas, incluso entre las fuentes canónicas. Mi terminología se extrae parcialmente de K & R y parcialmente del uso predominante en el primer departamento de CS en el que estudié /enseñé. Siempre es bueno escuchar otra vista informada.
    2013-12-28 22: 50: 47Z
  5. debes estar bromeando. ¿Realmente puedes definir una variable estática dentro de una función?
    2017-05-16 09: 57: 26Z

Los demás han respondido bastante bien a los trazos generales, por lo que les daré algunos detalles.

  1. La pila y el montón no necesitan ser singulares. Una situación común en la que tiene más de una pila es si tiene más de un hilo 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 como resultado que se asignen diferentes DLL de 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 alloca , 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 para un buffer de memoria virtual.

  3. Hacer un gran búfer temporal en Windows que no usas mucho no es gratis. Esto se debe a que el compilador generará un bucle de prueba de pila que se invoca cada vez que se ingresa a su función para asegurarse de que existe la pila (porque Windows usa una única página de protección al final de la pila para detectar cuándo debe aumentar la pila). Si accede a la memoria 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.
}
    
161
2017-07-30 11:18: 35Z
  1. Re "en lugar de alloc": ¿Quieres decir "en lugar de malloc"?
    2017-07-30 11: 19: 36Z
  2. ¿Qué tan portátil es alloca?
    2017-07-30 11: 20: 14Z
  3. @ PeterMortensen no es POSIX, no se garantiza la portabilidad.
    2017-11-01 17: 38: 27Z

Otros han respondido directamente a su pregunta, pero al tratar de entender la pila y el montón, creo que es útil considerar el diseño de memoria de un proceso tradicional de UNIX (sin subprocesos y asignadores basados ​​en mmap()). La página web Glosario de administración de memoria 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, hasta un tamaño establecido por el núcleo (que se puede ajustar con setrlimit(RLIMIT_STACK, ...)). El montón crece cuando el asignador de memoria invoca la llamada al sistema brk() o sbrk(), asignando más páginas de memoria física al espacio de direcciones virtuales del proceso.

En los 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 que se basan en microcontroladores PIC de Microchip), la pila de programas es un bloque de memoria independiente 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 programas (llamada, retorno, etc.). 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.

    
128
2015-08-05 20: 36: 31Z

La pila es una parte de la memoria que puede manipularse 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 volver a la pila) y regresar (regresar de una subrutina: esto saca la dirección de la pila y salta a ella). Es la región de la memoria debajo del registro de puntero de pila, que se puede configurar según sea necesario. La pila también se usa para pasar argumentos a subrutinas, y también para conservar los valores en los registros antes de llamar a subrutinas.

El montón es una parte de la memoria que se entrega a una aplicación por el sistema operativo, generalmente a través de una syscall como malloc. En los sistemas operativos modernos, esta memoria es un conjunto de páginas a las que solo tiene acceso el proceso de llamada.

El tamaño de la pila se determina en el tiempo de ejecución, y generalmente no crece después de que se inicie el programa. En un programa en C, la pila debe ser lo suficientemente grande como para contener cada variable declarada dentro de cada función. El montón crecerá dinámicamente según sea necesario, pero el sistema operativo finalmente está haciendo 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 tendrán que volver al núcleo para obtener más memoria. Este comportamiento es a menudo personalizable)

Debido a que ha asignado la pila antes de iniciar el programa, nunca necesita malloc antes de poder usar la pila, por lo que es una pequeña 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 la forma en que se implementan las páginas y dónde se almacenan es un detalle de la implementación.

    
109
2008-09-17 04: 29: 20Z
  1. También vale la pena mencionar aquí que Intel optimiza en gran medida los accesos de pila, especialmente cosas como predecir a dónde regresas de una función.
    2008-09-17 04: 37: 38Z

Creo que muchas otras personas le han dado la mayoría de las respuestas correctas sobre este asunto.

Un detalle que se ha pasado por alto, sin embargo, es que el "montón" en realidad debería llamarse "tienda libre". El motivo de esta distinción es que la tienda gratuita original se implementó con una estructura de datos conocida como "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, en este día moderno, la mayoría de las tiendas gratuitas se implementan con estructuras de datos muy elaboradas que no son montones binomiales.

    
109
2008-09-17 04: 57: 51Z
  1. Otra de las respuestas (ligeramente) implica que el lenguaje C requiere el uso de una "pila". Este es un error común, aunque es el paradigma (por mucho) dominante para implementar C99 6.2.4 automatic storage duration objects (variables). De hecho, la palabra "pila" ni siquiera aparece en el estándar de lenguaje C99: open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
    2009-09-01 05: 03: 55Z
  2. [@ Heath] Tengo un pequeño comentario sobre tu respuesta. Eche un vistazo a la respuesta aceptada a esta pregunta . Dice que la tienda libre muy probablemente es la misma que la pila , aunque no necesariamente lo es.
    2012-02-12 06: 34: 39Z

¿Qué es una pila?

Una pila es una pila de objetos, generalmente uno que está bien ordenado.

 Introduzca la descripción de la imagen aquí

  
    

Las pilas en las arquitecturas de computación son regiones de memoria donde los datos se agregan o eliminan de una manera de último en entrar, primero en salir.
    En una aplicación de múltiples subprocesos, cada subproceso tendrá su propia pila.

  

¿Qué es un montón?

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

 Introduzca la descripción de la imagen aquí

  
    

En las arquitecturas de computación, el montón es un área de memoria asignada dinámicamente que es administrada automáticamente por el sistema operativo o la biblioteca del administrador de memoria.
    La memoria en el montón se asigna, desasigna y cambia de tamaño regularmente durante la ejecución del programa, y ​​esto puede llevar a un problema llamado fragmentación.
    La fragmentación se produce cuando los objetos de memoria se asignan con pequeños espacios intermedios que son demasiado pequeños para contener objetos de memoria adicionales.
    El resultado neto es un porcentaje del espacio de almacenamiento dinámico que no se puede utilizar para otras asignaciones de memoria.

  

Ambos juntos

  
    

En una aplicación de múltiples subprocesos, 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 debe haber cierta coordinación entre los subprocesos para que no intenten acceder y manipular la misma (s) pieza (s) de memoria en el montón en al mismo tiempo.

  

¿Cuál 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.
    La asignación de 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 usar la pila, ya que es más fácil.
Debido a que la pila es pequeña, querrá usarla cuando sepa exactamente cuánta memoria necesitará para sus datos, o si sabe qué.El tamaño de tus datos es muy pequeño.
Es mejor usar el montón cuando sabe que necesitará mucha memoria para sus datos, o simplemente no está seguro de cuánta memoria necesitará (como con una matriz dinámica).

Modelo de memoria Java

 Introduzca la descripción de la imagen aquí

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 objeto, estas son simplemente referencias (punteros) a los objetos reales en el montón.
Cada vez que se crea una instancia de un objeto, una parte de la memoria del montón se reserva para mantener los datos (estado) de ese objeto. Como los objetos pueden contener otros objetos, algunos de estos datos pueden contener referencias a esos objetos anidados.

    
105
2017-07-30 12: 16: 46Z

Puedes hacer algunas cosas interesantes con la pila. Por ejemplo, tiene funciones como alloca (asumiendo que puede pasar las advertencias importantes con respecto a su use), 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 la pila son algunos de los peores que he experimentado. Si usa memoria de almacenamiento dinámico y sobrepasa los límites de su bloque asignado, tiene una buena oportunidad de desencadenar un fallo de segmento. (No es 100%: su bloque puede ser incidentalmente contiguo con otro que haya asignado previamente). Pero como las variables creadas en la pila siempre son contiguas entre sí, la escritura fuera de límites puede cambiar el valor de otra variable. He aprendido que siempre que siento que mi programa ha dejado de obedecer las leyes de la lógica, es probable que sea un desbordamiento de búfer.

    
87
2009-03-19 15: 55: 06Z
  1. ¿Qué tan portátil es alloca? Por ejemplo, ¿funciona en Windows? ¿Es solo para sistemas operativos tipo Unix?
    2017-07-30 12: 00: 54Z

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, ya veces los parámetros se insertan en la pila. Luego, cualquier variable local dentro de la subrutina se empuja hacia la pila (y se usa desde allí). Cuando la subrutina termina, todo eso vuelve a salir de la pila. La PC y los datos de registro se devuelven a su ubicación original, por lo que su programa puede continuar su camino.

El montón es el área de memoria. Las asignaciones de memoria dinámica están hechas de (llamadas explícitas "nuevas" o "asignar"). 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 distribuía de tal manera que el puntero de pila comenzó en la parte inferior de la memoria, el puntero del montón comenzó en la parte superior y crecieron uno hacia el otro. Si se superponen, estás fuera de la memoria RAM. Sin embargo, eso no funciona con los sistemas operativos modernos con múltiples subprocesos. Cada hilo debe tener su propia pila, y esos pueden crearse dinámicamente.

    
85
2009-03-19 15: 19: 30Z
  1. [@ T.E.D.] ¿Por qué dijiste "a veces los parámetros se insertan en la pila"? Lo que sé es que siempre son. ¿Podrías elaborar más?
    2012-02-12 06: 36: 42Z
  2. @ OmarOthman: lo digo porque depende completamente del escritor de su compilador /intérprete lo que sucede cuando se llama a una subrutina. El comportamiento clásico de Fortran es no usar una pila en absoluto. Algunos idiomas admiten cosas exóticas como el paso por nombre, que es efectivamente una sustitución textual.
    2012-04-03 15: 57: 07Z

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 de función suspendidas es la pila, porque los elementos de la pila (llamadas de función) dependen unos de otros.

Es importante tener en cuenta la pila en el manejo de excepciones y en las ejecuciones de subprocesos.

Montón

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

    
79
2017-07-30 12: 01: 46Z
  1. "Me gusta más la respuesta aceptada porque es un nivel aún más bajo". Eso es algo malo, no algo bueno.
    2017-04-25 09: 02: 17Z

Stack

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

Heap

  • Se puede acceder a las variables globalmente
  • No hay límite en el tamaño de la memoria
  • (Relativamente) acceso más lento
  • No se garantiza un uso eficiente del espacio, la memoria puede fragmentarse con el tiempo a medida que se asignan los bloques de memoria y luego se liberan
  • Debe administrar la memoria (está a cargo de asignar y liberar variables)
  • Las variables se pueden cambiar de tamaño usando realloc ()
50
2017-07-30 12: 14: 32Z

OK, simplemente y en pocas palabras, significan ordenado y no ordenado ...!

Pila : en los elementos de la pila, las cosas se superponen, significa que será más rápido y más eficiente de procesar. ...

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

Montón : no hay orden, el procesamiento será más lento y los valores se confunden sin un orden o índice específico ... hay aleatorios y no hay relación entre ellos ... así que la ejecución y el tiempo de uso puede variar ...

También creo la imagen de abajo para mostrar cómo pueden verse:

 introduce la descripción de la imagen aquí

    
42
2018-04-23 02: 06: 25Z

En resumen

Una pila se utiliza para la asignación de memoria estática y un montón para la asignación de memoria dinámica, ambos almacenados en la memoria 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 administra y optimiza muy de cerca. Cada vez que una función declara una nueva variable, se "empuja" a la pila. Entonces, cada vez que una función sale, todas las variables empujadas en la pila por esa función se liberan (es decir, se eliminan). Una vez que se libera una variable de pila, esa región de la memoria queda disponible para otras variables de pila.

La ventaja de usar la pila para almacenar variables, es que la memoria se administra para usted. No tiene que asignar memoria manualmente o liberarla una vez que ya no la necesite. Además, debido a que la CPU organiza la memoria de la pila de manera tan eficiente, leer y escribir para apilar las variables es muy rápido.

Puede encontrar más información aquí .


El montón

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

Si no lo hace, su programa tendrá lo que se conoce como una pérdida de memoria. Es decir, la memoria en el montón todavía se reservará (y no estará disponible para otros procesos). Como veremos en la sección de depuración, hay una herramienta llamada Valgrind que puede ayudarlo a detectar la memoria fugas.

A diferencia de la pila, el montón no tiene restricciones de tamaño en el 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 uno tiene que usar punteros para acceder a la memoria en el montón. Hablaremos de los punteros en breve.

A diferencia de la pila, las variables creadas en el montón son accesibles por cualquier función, en cualquier parte de su programa. Las variables del montón son esencialmente de alcance global.

Puede encontrar más información 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 trata 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 un orden LIFO, el bloque reservado más reciente es siempre el siguiente bloque que se liberará. Esto hace que sea realmente sencillo hacer 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 el 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 de forma aleatoria en cualquier momento. Puede asignar un bloque en cualquier momento y liberarlo en cualquier momento. Esto hace que sea mucho más complejo hacer un seguimiento de qué partes del montón están asignadas o son gratuitas en un momento dado.

 Introduzca la descripción de la imagen aquí

Puede usar la pila si sabe exactamente cuántos datos necesita asignar antes del tiempo de compilación, y no es demasiado grande. Puede usar el montón si no sabe exactamente cuántos datos necesitará en tiempo de ejecución o si necesita asignar una gran cantidad de 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 hilo y la pila es específica de la aplicación. Es importante tener en cuenta la pila en el manejo de excepciones y en las ejecuciones 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).

 Introduzca la descripción de la imagen aquí

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

Incluso, se proporcionan más detalles aquí y here .


Ahora ven a las respuestas de tu pregunta .

  

¿En qué medida están controlados por el sistema operativo o el tiempo de ejecución de idioma?

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

Puede encontrar más información aquí .

  

¿Cuál es su alcance?

Ya está en la parte superior.

  

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

Puede encontrar más información en aquí .

  

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

El tamaño de la pila se establece mediante OS cuando se crea un hilo. El tamaño del montón se establece en el inicio de la aplicación, pero puede aumentar 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 usar grupos de memoria, puede obtener un rendimiento comparable de la asignación de almacenamiento dinámico, pero eso viene con una ligera complejidad adicional y sus propios dolores de cabeza.

Además, la pila frente a la pila no es solo 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í .

    
40
2018-12-09 15: 12: 15Z

En la década de 1980, UNIX se propagó como conejitos con grandes compañías que se lanzaban. Exxon tenía uno, al igual que docenas de nombres de marcas perdidos en la historia. La forma en que se distribuyó la memoria fue a discreción de los muchos implementadores.

Un programa típico de C se colocó plano en la memoria con una oportunidad para aumentar cambiando el valor brk (). Normalmente, el HEAP estaba justo por debajo de este valor brk y el aumento de brk aumentó la cantidad de almacenamiento disponible.

El STACK individual era típicamente un área debajo de HEAP que era un tramo de memoria No contiene nada de valor hasta la parte superior del siguiente bloque de memoria fijo. Este siguiente bloque fue a menudo un CÓDIGO que podría ser sobrescrito por los datos de la pila en uno de los hacks famosos de su época.

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

El advenimiento de la memoria virtual en UNIX cambia muchas de las restricciones. No hay ninguna razón objetiva por la que estos bloques deban ser contiguos, O fijo en tamaño, o ordenado de una manera particular ahora. Por supuesto, antes de UNIX había Multics que no sufrían estas restricciones. Aquí hay un esquema que muestra uno de los diseños de memoria de esa era.

Un diseño de memoria de programa UNIX C de estilo típico de los años 80

    
35
2015-03-27 19: 55: 51Z

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

 pila, montón y datos estáticos

    
29
2018-03-12 18: 00: 15Z

Un par de centavos: creo que será bueno dibujar la memoria de manera gráfica y más simple:

 Esta es mi visión de la construcción de memoria de proceso con simplificación para una comprensión más sencilla de lo que está sucediendo


Flechas: muestra 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 de tamaño de la pila de hilos por los parámetros en la API de creación de hilos por lo general. Por lo general, el tamaño de la memoria virtual máxima del proceso es limitado, por ejemplo, para 32 bits 2-4 GB.

De manera sencilla: el montón de proceso es general para el proceso y todos los subprocesos internos, y se usa para la asignación de memoria en casos comunes con algo como malloc () .

La pila es una memoria rápida para almacenar en el caso común punteros y variables de retorno de función, procesados ​​como parámetros en la llamada de función, variables de función local.

    
24
2017-07-30 12: 22: 02Z

Debido a que algunas respuestas fueron delicadas, voy a contribuir con mi ácaro.

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

Las fibras, los hilos verdes y las coroutinas son similares en muchos aspectos, lo que lleva a mucha confusión. La diferencia entre las fibras y los hilos verdes es que los primeros utilizan la multitarea cooperativa, mientras que los últimos pueden presentar uno cooperativo o preventivo (o incluso ambos). Para la distinción entre fibras y corutinas, consulte aquí .

En cualquier caso, el propósito de ambas fibras, hilos verdes y coroutines es que múltiples funciones se ejecuten simultáneamente, pero no en paralelo (consulte this SO question para la distinción) dentro de un solo hilo a nivel del sistema operativo, transfiriendo el control entre sí de manera organizada .

Cuando usas fibras, hilos verdes o coroutines, usualmente tienes una pila separada por función. (Técnicamente, no solo una pila, sino todo el contexto de ejecución es por función. Lo más importante, los registros de la CPU). Para cada subproceso hay tantas pilas como funciones que se ejecutan simultáneamente, y el subproceso está cambiando entre la ejecución de cada función Según la lógica de tu programa. Cuando una función llega a su fin, su pila se destruye. Por lo tanto, el número y la duración de las pilas son dinámicos y no están determinados por el número de subprocesos a nivel del sistema operativo.

Tenga en cuenta que dije " generalmente tiene una pila por función". Hay implementaciones de stackful y stackless de couroutines. Las implementaciones apilables más notables de C ++ son Boost.Coroutine y PPL de Microsoft async/await. (Sin embargo, las funciones de reanudación de C ++ (alias "async y await"), que se propusieron a C ++ 17, es probable que usen coroutines sin apilamiento.

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

    
21
2017-05-23 11: 55: 01Z

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

Pila

  • Acceso muy rápido.
  • Almacenado en RAM.
  • Las llamadas de función 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 se sale de un ámbito.
  • Almacenado en la memoria secuencial.

Heap

  • Acceso lento en comparación con Stack.
  • Almacenado en RAM.
  • Las variables creadas dinámicamente se almacenan aquí, que más tarde requieren liberar la memoria asignada después del uso.
  • Se almacena donde sea que se realice la asignación de memoria, se accede siempre con el puntero.

Nota interesante:

  • Si las llamadas a la función se hubieran almacenado en el montón, habrían resultado en 2 puntos desordenados:
    1. Debido al almacenamiento secuencial en la pila, la ejecución es más rápida. El almacenamiento en el montón habría provocado un gran consumo de tiempo, por lo que todo el programa se ejecutaría más lentamente.
    2. Si las funciones se almacenaran en el montón (almacenamiento desordenado apuntado con un puntero), no habría habido manera de volver a la dirección de la persona que llama (qué pilada debido al almacenamiento secuencial en la memoria).
14
2018-12-10 08: 23: 58Z
  1. conciso y limpio. bonito :)
    2018-12-05 19: 14: 07Z

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

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

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

  • Sin la pila ningún microprocesador puede funcionar. (No podemos imaginar un programa, incluso en lenguaje ensamblador, sin subrutinas /funciones)
  • Sin el montón puede. (Un programa de lenguaje ensamblador puede funcionar sin él, ya que el montón es un concepto de sistema operativo, como malloc, que es una llamada de sistema operativo /Lib.

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

  • 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) ejecutando algún código y administrar algunas estructuras necesarias para realizar un seguimiento de la asignación.
8
2017-08-07 08: 27: 43Z
  1. ¿Qué es OPP? ¿Se refiere a la POO ( programación_internacional orientada a objetos )?
    2017-07-30 12: 34: 40Z
  2. ¿Quieres decir que malloc es una llamada al kernel?
    2017-07-30 12: 35: 56Z
  3. 1) sí, lo siento ... OOP ... 2) malloc: Escribo brevemente, lo siento ... malloc está en el espacio del usuario ... pero puede desencadenar otros llamadas .... el punto es que el uso del montón PUEDE ser muy lento ...
    2017-08-03 12: 18: 19Z

¡Guau! Tantas respuestas y no creo que una de ellas lo haya hecho bien ...

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

La pila es una memoria que comienza como la dirección de memoria más alta asignada a la imagen de su programa y luego disminuye su valor desde allí. Se reserva para los parámetros de función llamados y para todas las variables temporales utilizadas en las funciones.

Hay dos montones: público y privado.

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

Si el montón privado se vuelve demasiado grande, se superpondrá al área de la pila, al igual que la pila se superpondrá al montón si es demasiado grande. Debido a que la pila comienza en una dirección más alta y se abre camino hacia una dirección más baja, con la piratería adecuada puede hacer que la pila sea tan grande que supere el área del montón privado y se superponga al área del código. El truco entonces es superponer suficiente del área del código que puede enganchar en el código. Es un poco difícil de hacer y corre el riesgo de que se produzca un bloqueo del programa, pero es fácil y muy eficaz.

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 SO o el tiempo de ejecución de idioma?

La pila es controlada por el programador, el montón privado es administrado por el sistema operativo y el montón público no es controlado por nadie porque es un servicio del sistema operativo: usted realiza solicitudes y se otorgan o rechazan.

2b) ¿Cuál es su alcance?

Todos son globales para el programa, pero sus contenidos pueden ser privados, públicos o globales.

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

El tamaño de tLa pila y el 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 utilizando 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"

REF:

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

    
8
2019-02-20 02: 04: 38Z
fuente colocada aquí