memory-management - ¿Qué y dónde están la pila y el montón?


Los libros de programación de idiomas explican que los tipos de valores 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é son (físicamente en la memoria de una computadora real)?

  • ¿En qué medida están controlados por el sistema operativo o el tiempo de ejecución del idioma?
  • ¿Cuál es su alcance?
  • ¿Qué determina el tamaño de cada uno de ellos?
  • ¿Qué lo hace a uno más rápido?

Answers



La pila es la memoria reservada como espacio cero 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 las variables locales y algunos datos de contabilidad. Cuando esa función retorna, el bloque no se usa 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 reciente es siempre el siguiente bloque que se liberará. Esto hace que sea realmente simple 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 reservada 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 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 hilo obtiene una pila, mientras que normalmente solo hay un montón para la aplicación (aunque no es raro tener múltiples montones para diferentes tipos de asignación).

Para responder a sus preguntas directamente:

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

El sistema operativo asigna la pila para cada subproceso de nivel del sistema cuando se crea el subproceso. Normalmente, el lenguaje de ejecución 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 se recupera la pila. El montón se suele asignar al inicio de la aplicación por el tiempo de ejecución y se recupera cuando finaliza la aplicación (proceso técnico).

¿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 almacenamiento dinámico se establece al inicio de la aplicación, pero puede crecer a medida que se necesita espacio (el asignador solicita más memoria del sistema operativo).

¿Qué lo hace a uno 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 desde allí (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 al caché del procesador, lo que lo hace muy rápido. Otro golpe de rendimiento para el montón es que el montón, que es principalmente un recurso global, normalmente tiene que ser seguro para varios subprocesos, es decir, cada asignación y desasignación debe sincronizarse, por lo general, con "todos" otros accesos de montón en el programa.

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




Apilar:

  • Almacenado en la memoria RAM de la computadora al igual que el montón.
  • Las variables creadas en la pila saldrán del alcance y se desasignarán automáticamente.
  • Mucho más rápido de asignar en comparación con las variables en el montón.
  • Implementado con una estructura de datos de pila real.
  • Almacena datos locales, direcciones de retorno, usadas para el paso de parámetros.
  • Puede tener un desbordamiento de pila cuando se utiliza una gran parte de la pila (principalmente de recursividad infinita o demasiado profunda, asignaciones muy grandes).
  • Los datos creados en la pila se pueden usar sin punteros.
  • Utilizaría la pila si sabe exactamente la cantidad de datos que necesita asignar antes del tiempo de compilación y no es demasiado grande.
  • Usualmente tiene un tamaño máximo ya determinado cuando se inicia su programa.

Montón:

  • 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 del alcance. Los datos se liberan con delete , delete[] o free .
  • Más lento para asignar en comparación con las variables en la pila.
  • Usado 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 se señalarán mediante punteros y se asignarán con new o malloc respectivamente.
  • Puede tener fallas de asignación si se solicita que se asigne 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 una gran cantidad de datos.
  • Responsable de 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;



El punto más importante es que el montón y la pila son términos genéricos de 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 se colocaron allí, y solo se puede quitar el superior (sin volcar todo el asunto).

    La simplicidad de una pila es que no necesita mantener una tabla que contenga un registro de cada sección de memoria asignada; la única información de estado que necesita es un solo 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 la memoria y extenderla 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 artículos. Puede acceder y eliminar elementos en cualquier orden porque no hay un elemento claro "superior".

    La asignación de montón requiere mantener un registro completo de qué memoria se asigna y qué no, así como también un mantenimiento general para reducir la fragmentación, encontrar segmentos de memoria contiguos lo suficientemente grandes para ajustarse al tamaño solicitado, y así sucesivamente. La memoria puede ser desasignada en cualquier momento dejando espacio libre. Algunas 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 cuando la memoria ya no está en el alcance y deslocalándola.

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

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

    Como se mencionó, el montón y la pila son términos generales, y se pueden implementar de muchas maneras. Los programas de computadora generalmente tienen 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. Como las funciones llaman a otras funciones y luego regresan, la pila crece y se contrae 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 es típicamente asignada por el sistema operativo, con la aplicación que llama a las funciones API para hacer esta asignación. Se requiere un poco de sobrecarga en la administración de 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 bajo nivel que no se relaciona con el 'alcance' en el sentido de programación. Si desensambla algún código, verá referencias de estilo de puntero relativo a porciones de la pila, pero en lo que se refiere 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 retorna, cualquier elemento local de esa función se libera inmediatamente de la pila. Eso funciona de la manera en que esperaría que funcione 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 expone el sistema operativo, pero su lenguaje de programación probablemente agrega sus reglas sobre qué es un "alcance" en su aplicación. La arquitectura del procesador y el SO utilizan el direccionamiento virtual, que el procesador traduce a direcciones físicas y hay fallas de página, etc. Se mantienen al tanto de qué páginas pertenecen a qué aplicaciones. Sin embargo, nunca tendrá que preocuparse por esto, simplemente use el método que use su lenguaje de programación para asignar y liberar memoria y verifique 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, compilador, sistema operativo y arquitectura. Una pila generalmente se asigna previamente, porque por definición debe ser memoria contigua (más sobre esto en el último párrafo). El compilador de lenguaje 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 utilice por completo, excepto en casos de recursión interminable no deseada (por lo tanto, "desbordamiento de la pila") u otras decisiones inusuales de programación.

    Un montón es un término general para todo lo que se puede asignar dinámicamente. Según la forma en que lo mires, cambia constantemente de tamaño. En los procesadores modernos y sistemas operativos, la forma exacta en que funciona está muy abstraída de todos modos, por lo que normalmente no tendrá que preocuparse mucho sobre cómo funciona en el fondo, excepto que (en los idiomas donde lo permita) no debe usar memoria que usted no ha asignado todavía o la memoria que ha liberado.

  • ¿Qué lo hace a uno más rápido?

    La pila es más rápida porque toda la memoria libre siempre es 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 especial y rápido para este propósito. Además, las operaciones posteriores en una pila normalmente se concentran en áreas de memoria muy cercanas, lo que a un nivel muy bajo es bueno para la optimización de los cachés en memoria 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 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 asignada 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 concurrente 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 a partir 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, los bloques nuevos a menudo se asignan desde direcciones inferiores hacia 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.
  • Asignar y desasignar muchos bloques pequeños puede dejar el montón en un estado en el que hay muchos pequeños bloques 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 como para satisfacer la solicitud de asignación, aunque el tamaño combinado de los bloques libres puede ser lo suficientemente grande. Esto se llama fragmentación de montón .
  • Cuando un bloque usado que está adyacente a un bloque libre es desasignado, el nuevo bloque libre puede fusionarse con el bloque libre adyacente para crear un bloque libre más grande que reduce efectivamente la fragmentación del montón.

La pila

  • La pila a menudo funciona en tándem con un registro especial en la CPU llamado puntero de la pila . Inicialmente, el puntero de 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 a la pila y sacarlos de la pila. Cada inserción 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 la pila y luego aumenta el puntero de la pila (no se confunda por el hecho de que al agregar un valor a la pila disminuye el puntero de la pila y la eliminación de un valor aumenta . Recuerde que la pila crece 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 presionan el puntero de instrucción actual, es decir, la dirección del código que se ejecuta en la pila. Luego, la CPU salta a la función configurando el puntero de instrucción en la dirección de la función llamada. Más tarde, cuando la función retorna, el puntero de instrucción anterior se saca 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 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 retorna, el puntero de la pila se mueve hacia atrás para liberar el área asignada.
  • Si una función tiene parámetros, estos se envían a la pila antes de la llamada a la función. El código en la función es capaz de navegar por la pila desde el puntero de la pila actual para localizar estos valores.
  • Las llamadas a función de anidación funcionan como un amuleto. 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 las llamadas anidadas y se desenrollarán de la manera correcta cuando regresen las funciones.
  • Como la pila es un bloque limitado de memoria, puede causar un desbordamiento de la 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 activará una trampa 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.

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

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

Cómo se gestiona el montón depende realmente del entorno de tiempo 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 más de 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 a la biblioteca que maneja el montón. Sin embargo, crecer la pila es a menudo imposible ya que el desbordamiento de la pila solo se descubre cuando ya es demasiado tarde; y cerrar el hilo de la 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í es como se maneja la memoria

Local Variables que solo necesitan durar tanto como la invocación de función entre en la pila. El montón se usa para variables cuya duración no conocemos por adelantado, pero esperamos que duren un tiempo. En la mayoría de los lenguajes es crítico que sepamos en tiempo de 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 en el montón porque no sabemos en el momento de la creación cuánto durarán. En muchos idiomas, el montón es basura recolectada 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 idiomas como C / C ++, las estructuras y las clases a menudo pueden permanecer en la pila cuando no se trata de punteros.

Más información se puede encontrar aquí:

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

y aquí:

Creando objetos en la pila y el montón

Este artículo es la fuente de la imagen anterior: Seis conceptos .NET importantes: pila, montón, tipos de valores, tipos de referencia, boxeo y desempaquetado - CodeProject

pero ten en cuenta que puede contener algunas inexactitudes.




La pila Cuando llamas a una función, los argumentos a esa función más algunos otros gastos generales se ponen en la pila. También se almacena cierta información (por ejemplo, dónde ir para regresar). Cuando declaras una variable dentro de tu 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 apiladas se agregan a medida que ingresas funciones, los datos correspondientes se eliminan al salir de ellos. Esto significa que tiende a permanecer dentro de una pequeña región de la pila a menos que llame a muchas funciones que invocan muchas otras funciones (o crea una solución recursiva).

El montón El 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 creará su programa, es probable que utilice el nuevo operador (o malloc o equivalente) para crear cada nave espacial. Esta asignación se mantendrá por 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 son: la memoria se fragmenta. Encontrar memoria libre del tamaño que necesita es un problema difícil. Esta es la razón por la que se debe evitar el montón (aunque todavía se usa con frecuencia).

Implementación La implementación tanto de la pila como del montón suele ser hasta el 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 solo es práctico si el uso de la memoria es bastante diferente de la norma, es decir, para juegos donde se carga un nivel en una operación enorme y se puede tirar todo el lote 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 que hace que tu programa piense que tienes acceso a cierta 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 para el montón son impredecibles (es decir, implícitas específicas) y francamente no son importantes.




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

Las variables "estáticas" (AKA estáticamente asignadas) no están asignadas en la pila. No asuma eso, muchas personas lo hacen solo 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 el segmento de datos .

Sin embargo, generalmente es mejor considerar " alcance " y " vida " en lugar de "apilar" y "acumular".

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

Lifetime se refiere a cuando una variable es asignada y desasignada durante la ejecución del programa. Por lo general, pensamos en la asignación estática (la variable persistirá durante toda la duración del programa, por lo que es útil para almacenar la misma información en varias llamadas a funciones) frente a la asignación automática (la variable solo persiste durante una llamada a una función, lo cual es útil para almacenar información que solo se usa durante su función y puede descartarse una vez que haya terminado) versus la asignación dinámica (variables cuya duración se define en tiempo de ejecución, en lugar de tiempo de compilación 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 usar stacks, 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 existan en la pila. Como se ha señalado en algunos comentarios, es libre de implementar un compilador que ni siquiera usa una pila o un montón, sino otros mecanismos de almacenamiento (raramente, ya que las pilas y montones son excelentes para esto).

Proporcionaré un código C anotado simple para ilustrar todo esto. La mejor forma de aprender es ejecutar un programa bajo un depurador y observar el comportamiento. Si prefiere leer Python, salte 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 punzante de por qué es importante distinguir entre vida y el alcance es que una variable puede tener un alcance local, pero toda la vida estática - por ejemplo, "someLocalStaticVariable" en la muestra de código de seguridad. Estas variables pueden hacer que nuestros hábitos de nomenclatura común, aunque informal, muy confuso. Por ejemplo, cuando decimos " locales " nos referimos casi siempre " variables de ámbito local asignado automáticamente " y cuando decimos mundial normalmente queremos decir " un ámbito global asignada estáticamente variables ". Por desgracia, cuando se trata de cosas como " archivo de ámbito de las variables asignadas estáticamente " mucha gente acaba de decir ... " eh ??? ".

Algunas de las opciones de sintaxis en C / C ++ exacerbar este problema - por ejemplo, muchas personas piensan que las variables globales no son "estática" 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 impide var2 de tener alcance mundial. Sin embargo, el var1 global tiene la asignación estática. Esto no es intuitivo! Por esta razón, trato de no usar la palabra "estática" al describir el alcance, y en lugar de decir algo como "archivo" o "limitado archivo" radio de acción. Sin embargo, muchas personas utilizan la frase "estática" o "ámbito estático" para describir una variable que sólo se puede acceder desde un archivo de código. En el contexto de toda la vida, "estática" siempre significa que la variable se asigna al inicio del programa y desasigna cuando sale del programa.

Algunas personas piensan de estos conceptos como C / C ++ específico. Ellos no son. Por ejemplo, la muestra de Python a continuación ilustra los tres tipos de asignación (hay algunas diferencias sutiles posibles en lenguajes interpretados que no voy a entrar en 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 a los grandes rasgos bastante bien, así que voy a lanzar en algunos detalles.

  1. Pila y el montón no tiene que ser singular. Una situación común en el que hay más de una pila es si usted 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 DLL pueden dar lugar a diferentes DLL asignación de diferentes montones, que es por eso que es generalmente una mala idea para liberar memoria asignada por una biblioteca diferente.

  2. En C se puede obtener el beneficio de la asignación de longitud variable a través del uso de alloca , que asigna en la pila, en contraposición a alloc, que asigna en el montón. Esta memoria no sobrevivirá a su estado de retorno, pero es útil para un buffer de cero.

  3. Hacer un buffer temporal enorme en Windows que no utiliza gran parte no es libre. Esto es porque el compilador generará un bucle sonda de pila que se llama cada vez que se introduce la función para asegurarse de que existe la pila (ya que Windows utiliza una única página de protección al final de su pila para detectar cuando se necesita para crecer la pila. Si accede a la memoria más de una página fuera de la 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 cuando se trata de entender la pila y el montón, creo que es útil tener en cuenta el diseño de memoria de un proceso UNIX tradicional (sin hilos y mmap()asignadores basados). La Memoria de Gestión Glosario de la página web tiene un diagrama de este diseño de memoria.

La pila y el montón son tradicionalmente situados 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 el brk()o sbrk()llamada al sistema, la cartografía más páginas de memoria física en el espacio virtual de direcciones del proceso.

En los sistemas sin memoria virtual, tales como algunos sistemas embebidos, el mismo diseño básico a menudo se aplica, excepto la pila y montón se fijan en tamaño. Sin embargo, en otros sistemas empotrados (tales como los basados en microcontroladores PIC de Microchip), la pila del programa es un bloque separado de la memoria que no es direccionable por instrucciones de movimiento de datos, y sólo puede ser modificado o leer indirectamente a través de instrucciones de flujo de programa (llamar, 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.




Creo que muchas otras personas que han dado respuestas correctas sobre todo en esta materia.

Un detalle que ha pasado por alto, sin embargo, es que el "montón" debería, de hecho, es probable que se llama la "tienda libre". La razón de esta diferencia es que la tienda libre original fue implementado con una estructura de datos conocida como "montón binomial." Por esa razón, la asignación de las primeras implementaciones de malloc () / free () fue la asignación de un montón. Sin embargo, en estos tiempos modernos, la mayoría de las tiendas libres se implementan con estructuras de datos muy elaborados que no son montones binomiales.




La pila es una porción de memoria que puede ser manipulado a través de varias instrucciones en lenguaje ensamblador clave, como 'pop' (quitar y devolver un valor de la pila) y 'empuje' (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 retorno (retorno de una subrutina - esto aparece la dirección fuera de la pila y salta a ella). Es la región de la memoria por debajo del registro del puntero de pila, que se puede ajustar según sea necesario. La pila también se usa para pasar argumentos a subrutinas, y también para la preservación de los valores en los registros antes de llamar a las subrutinas.

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

El tamaño de la pila se determina en tiempo de ejecución, y por lo general no crece después de que el programa se inicia. 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á de forma dinámica según sea necesario, pero el sistema operativo está en última instancia, hacer la llamada (que a menudo crecer el montón por más que el valor solicitado por malloc, para que al menos algunos mallocs futuras no tendrán que volver a que el núcleo obtener más memoria. Este comportamiento es a menudo personalizable)

Debido a que ha asignado la pila antes de lanzar el programa, que no será necesario malloc antes de poder utilizar la pila, así que es una ligera ventaja allí. En la práctica, es muy difícil predecir lo que va a ser rápido y lo que va a ser lento en los sistemas operativos modernos que tienen subsistemas de memoria virtual, debido a la forma en que las páginas se implementan y en el que se almacenan es un detalle de implementación.




¿Qué es una pila?

Una pila es una pila de objetos, típicamente uno que está dispuesta ordenadamente.

Pilas en arquitecturas informáticas son regiones de memoria en la que se añade o elimina datos de una manera último en entrar, primero en salir.
En una aplicación multi-hilo, cada hilo tendrá su propia pila.

¿Qué es un montón?

Un montón es una colección desordenada de cosas amontonadas sin orden ni concierto.

En las arquitecturas de computación del montón es un área de memoria asignada dinámicamente que se gestiona automáticamente por el sistema operativo o la biblioteca del gestor de memoria.
La memoria en el montón se asigna, cancela la asignación, y cambiar de tamaño regular durante la ejecución del programa, y esto puede llevar a un problema que se llama fragmentación.
La fragmentación se produce cuando los objetos de memoria se asignan con pequeños espacios entre que son demasiado pequeño para contener objetos de memoria adicionales.
El resultado neto es un porcentaje del espacio de montón que no es utilizable para otras asignaciones de memoria.

Ambos juntos

En una aplicación multi-hilo, cada hilo tendrá su propia pila. Sin embargo, todos los diferentes hilos compartirán el montón.
Debido a los diferentes hilos comparten el montón en una aplicación multi-hilo, esto también significa que tiene que haber algún tipo de coordinación entre los hilos para que no tratan de acceder y manipular la misma pieza (s) 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ápido que el montón.
Esto es debido a la forma en que la memoria se asigna en la pila.
La asignación de memoria en la pila es tan sencillo como mover la pila puntero hacia arriba.

Para las personas nuevas a la programación, que es probablemente una buena idea utilizar la pila, ya que es más fácil.
Debido a que la pila es pequeño, usted quiere usarlo cuando se sabe exactamente la cantidad de memoria que necesita para sus datos, o si se conoce el tamaño de sus datos es muy pequeño.
Es mejor utilizar el montón cuando se sabe que se necesita una gran cantidad de memoria para los datos, o simplemente no está seguro de la cantidad de memoria que se necesita (como con una matriz dinámica).

Java modelo de memoria

La pila es el área de memoria donde se almacenan las variables locales (incluyendo los parámetros del método). Cuando se trata de las variables de objeto, éstos no son más que referencias (punteros) a los objetos reales en el montón.
Cada vez que un objeto se crea una instancia, un trozo de memoria de pila se ha reservado para contener los datos (estado) de ese objeto. Dado que los objetos pueden contener otros objetos, algunos de estos datos puede, de hecho, contener referencias a esos objetos anidados.




Usted puede hacer algunas cosas interesantes con la pila. Por ejemplo, usted tiene funciones como alloca (suponiendo que usted puede conseguir más allá de las advertencias copiosas relativas a su uso), que es una forma de malloc que utiliza específicamente la pila, no el montón, para la memoria.

Dicho esto, los errores de memoria basada en la pila son algunos de los peores que he experimentado. Si utiliza la pila de memoria, y que sobrepasan los límites de su bloque asignado, usted tiene una buena posibilidad de desencadenar un fallo segmento. (No es 100%: el bloque puede ser por cierto contigua a otra que haya asignado previamente.) Pero ya que las variables creadas en la pila siempre son contiguas entre sí, escribiendo fuera de límites puede cambiar el valor de otra variable. He aprendido que cuando siento que mi programa ha dejado de obedecer las leyes de la lógica, es probable desbordamiento de búfer.




Simplemente, la pila es donde se crean las variables locales. Además, cada vez que se llama a una subrutina el contador de programa (puntero a la siguiente instrucción de máquina) y cualesquiera registros importantes, ya veces los parámetros son empujados en la pila. Entonces, cualquier variables locales dentro de la subrutina se insertan en la pila (y utilizados desde allí). Cuando termina la subrutina, eso todo se metió detrás de la pila. El PC y el registro de los datos se y volver a poner donde estaba, ya que está elevado, por lo que su programa puede seguir su camino feliz.

El montón es el área de las asignaciones de memoria dinámica de memoria están hechos de (explícita "nuevo" o "asignar" llamadas). Es una estructura de datos especial que puede realizar un seguimiento de los bloques de memoria de diferentes tamaños y de su estado de asignación.

En los sistemas "clásicos" de la RAM se colocó de tal manera que el puntero de pila se inició en la parte inferior de la memoria, el puntero de pila se inició en la parte superior, y que creció uno hacia el otro. Si se superponen, se encuentra fuera de RAM. Eso no funciona con los sistemas operativos modernos multi-hilo sin embargo. Cada hilo tiene que tener su propia pila, y los puede conseguir creada dinámicamente.




De WikiAnwser.

Apilar

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 aquellas funciones que sigue en suspenso hasta la última función devuelve su valor.

Esta cadena de llamadas a funciones en suspensión es la pila, porque los elementos de la pila (llamadas de función) dependen unos de otros.

La pila es importante tener en cuenta en el manejo de excepciones e hilo ejecuciones.

Montón

El montón es simplemente la memoria utilizada por programas para almacenar variables. Elemento de la pila (variables) no tienen dependencias entre sí y siempre se puede acceder aleatoriamente en cualquier momento.




Apilar

  • acceso muy rápido
  • No tienen que explícitamente desasignar las variables
  • El espacio está gestionado de forma eficiente por la CPU, la memoria no se fragmentará
  • Las variables locales sólo se
  • Límite en el tamaño de la pila (OS-dependiente)
  • Las variables no pueden ser redimensionados

Montón

  • Las variables pueden ser de acceso global
  • No hay límite en el tamaño de la memoria
  • (Relativamente) un acceso más lento
  • No se garantiza un uso eficiente del espacio, la memoria puede fragmentarse con el tiempo que se asignan bloques de memoria, y luego liberado
  • Debe gestionar la memoria (que está a cargo de asignar y liberar las variables)
  • Las variables se pueden clasificar según el tamaño usando realloc ()



En la década de 1980, UNIX propaga como conejos con grandes empresas rodar su propia cuenta. Exxon tenía uno igual que docenas de marcas perdido para la historia. ¿Cómo fue presentada la memoria era a discreción de los muchos ejecutores.

Un programa típico C fue el plano presentado en la memoria con la oportunidad de aumentar cambiando el valor brk (). Típicamente, el montón era justo por debajo de este valor brk y aumentando brk aumentó la cantidad de almacenamiento dinámico disponible.

La única pila era típicamente un área por debajo HEAP que era una zona de la memoria que contiene nada de valor hasta que la parte superior del siguiente bloque de memoria fija. Este bloque siguiente era a menudo CÓDIGO que podría ser ocupado por datos de pila en uno de los famosos cortes de su época.

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

El advenimiento de la memoria virtual en UNIX cambia muchas de las restricciones. No hay ninguna razón objetiva para que estos bloques deben ser contiguos, o un tamaño fijo, o solicitarse una manera particular ahora. Por supuesto, antes de UNIX fue Multics que no sufren de estas limitaciones. Aquí es un esquema que muestra uno de los diseños de memoria de aquella época.




  • Introducción

La memoria física es el rango de las direcciones físicas de las células de memoria en el que una aplicación o sistema almacena sus datos, código, y así sucesivamente durante la ejecución. Gestión de la memoria denota el manejo de estas direcciones físicas mediante el canje de los datos de la memoria física a un dispositivo de almacenamiento y luego de vuelta a la memoria física cuando sea necesario. El sistema operativo implementa los servicios de gestión de memoria que utilizan la memoria virtual. Como desarrollador de aplicaciones C # no es necesario escribir cualquiera de los servicios de gestión de memoria. El CLR utiliza los servicios de gestión de la memoria del sistema operativo subyacente para proporcionar el modelo de memoria para C # o cualquier otro lenguaje de alto nivel de orientación al CLR.

La figura 4-1 muestra la memoria física que se ha abstraído y gestionado por el sistema operativo, utilizando el concepto de memoria virtual. La memoria virtual es la visión abstracta de la memoria física, gestionado por el sistema operativo. La memoria virtual es simplemente una serie de direcciones virtuales, y estas direcciones virtuales se traducen por la CPU en la dirección física cuando sea necesario.

La Figura 4-1. Abstracción de memoria CLR

El CLR proporciona la capa abstracta gestión de memoria para el entorno de ejecución virtual, utilizando los servicios de la memoria operativa. Los conceptos abstractos son los usos CLR dominio de aplicación, hilo, pila, archivo heapmemorymapped, y así sucesivamente. El concepto del dominio de aplicación (AppDomain) da a su aplicación un entorno de ejecución aislado.

  • La interacción entre la memoria CLR y OS

Al observar el seguimiento de la pila mientras se depura la siguiente aplicación C #, utilizando WinDbg, verá cómo el CLR utiliza los servicios de gestión de memoria del sistema operativo subyacente (por ejemplo, el método de HeapFree KERNEL32.dll, el método de RtlpFreeHeap ntdll.dll) para poner en práctica su propio modelo de memoria:

using System;
namespace CH_04
{
    class Program
    {
        static void Main(string[] args)
        {
            Book book = new Book();
            Console.ReadLine();
        }
    }

    public class Book
    {
        public void Print() { Console.WriteLine(ToString()); }
    }
}

El ensamblado compilado del programa se carga en WinDbg iniciar la depuración. Se utilizan los siguientes comandos para inicializar la sesión de depuración:

0: 000> sxe ld clrjit

0: 000> g

0: 000> .loadby sos CLR

0: 000> .load C: \ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ sos.dll

A continuación, se establece un punto de interrupción en el método principal de la clase de programa, utilizando el comando bpmd:

0: 000> bpmd CH_04.exe CH_04.Program.Main

Para continuar la ejecución y descanso en el punto de interrupción, ejecutar el comando G:

0: 000> g

Cuando se rompe la ejecución en el punto de interrupción, se utiliza el comando eestack! Para ver los detalles seguimiento de la pila de todos los subprocesos que se ejecutan en el proceso actual. La siguiente salida muestra el seguimiento de la pila para todos los subprocesos que se ejecutan para la aplicación CH_04.exe:

0: 000> eestack

0 hilo

cuadro actual: (0 MethodDesc 00233800 CH_04.Program.Main (System.String []))

ChildEBP RetAddr de llamadas, destinatario de la llamada

0022ed24 CLR 5faf21db! CallDescrWorker + 0x33

/ TRACE eliminado /

0022f218 77712d68 ntdll! RtlFreeHeap + 0x142, llamando ntdll! RtlpFreeHeap

0022f238 771df1ac KERNEL32! HeapFree + 0x14, llamando ntdll! RtlFreeHeap

0022f24c 5fb4c036 CLR! EEHeapFree + 0x36, llamando KERNEL32! HeapFree

0022f260 5fb4c09d CLR! EEHeapFreeInProcessHeap + 0x24, llamando CLR! EEHeapFree

0022f274 5fb4c06d CLR! Operador delete [] + 0x30, llamando CLR! EEHeapFreeInProcessHeap / trazas retira /

0022f4d0 7771316f ntdll! RtlpFreeHeap + 0xb7a, llamando ntdll! _SEH_epilog4

0022f4d4 77712d68 ntdll! RtlFreeHeap + 0x142, llamando ntdll! RtlpFreeHeap

0022f4f4 771df1ac KERNEL32! HeapFree + 0x14, llamando ntdll! RtlFreeHeap

/ TRACE eliminado /

Este seguimiento de la pila indica que el CLR utiliza los servicios de gestión de memoria del sistema operativo para ejecutar su propio modelo de memoria. Cualquier operación de memoria in.NET pasa a través de la capa de memoria CLR para la capa de gestión de memoria del sistema operativo.

Figura 4-2 ilustra un C # modelo de memoria aplicación típica usada por el CLR en tiempo de ejecución.

Figura 4-2 . Un modelo de memoria # aplicación típica C

El modelo de memoria CLR está estrechamente unida con los servicios de gestión de memoria del sistema operativo. Para entender el modelo de memoria CLR, es importante entender el modelo de memoria del sistema operativo subyacente. También es importante saber cómo el espacio de direcciones de memoria física se abstrae en el espacio de direcciones de memoria virtual, las formas en el espacio de direcciones virtuales está siendo utilizado por la aplicación de usuario y la aplicación del sistema, la forma virtual a física trabaja asignación de dirección, cómo la memoria -mapped archivo funciona, y así sucesivamente. Este conocimiento de fondo va a mejorar su comprensión de los conceptos de modelo de memoria CLR, incluyendo dominio de aplicación, pila, y el montón.

Para obtener más información, consulte este libro:

C # deconstruido: Descubre cómo funciona C # en .NET Framework

Este libro + # + ClrViaC Windows internas son excelentes recursos para .NET Framework conocido en profundidad y la relación con el sistema operativo.




En especie

Una pila se utiliza para la asignación de memoria estática y un montón de asignación de memoria dinámica, tanto almacena en la memoria RAM del ordenador.

En detalle

La pila

La pila es una "LIFO" (último en entrar, primero en salir) estructura de datos, que es administrado y optimizado por la CPU muy de cerca. Cada vez que una función declara una nueva variable, se "empuja" en la pila. Entonces cada vez que sale de una función, todas las variables inserta en la pila por esa función, son liberados (es decir, se eliminan). Una vez que se libera una variable de pila, esa región de memoria disponible para otras variables de pila.

La ventaja de utilizar la pila para almacenar variables, es que la memoria se gestiona para usted. Usted no tiene que asignar memoria a mano, o libre una vez que no lo necesita más. Lo que es más, ya que la CPU organiza memoria de pila de manera eficiente, la lectura y escritura para apilar variables es muy rápido.

Más se pueden encontrar aquí .

el Montón

El montón es una región de la memoria del ordenador que no se gestiona automáticamente para usted, y no está tan firmemente manejado por la CPU. Es una región más libre flotación de la memoria (y es más grande). Para asignar memoria en el montón, debe utilizar malloc () o calloc (), que están incorporados en funciones C. Una vez que haya asignado la memoria en el montón, que son responsables de la utilización libre () para cancelar la asignación de memoria que una vez que no lo necesita 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, existe una herramienta llamada Valgrind que pueden ayudarle a detectar pérdidas de memoria.

A diferencia de la pila, la pila no tiene restricciones de tamaño de tamaño variable (aparte de las limitaciones físicas evidentes de su ordenador). memoria de almacenamiento dinámico es un poco más lento para ser leídos y escritos, porque uno tiene que utilizar punteros para acceder a la memoria en el montón. Vamos a hablar de los punteros en breve.

A diferencia de la pila, las variables creadas en el montón se puede acceder en cualquier función, en cualquier parte de su programa. las variables son esencialmente montón de alcance mundial.

Más se pueden encontrar aquí .

Las variables asignados 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 aquellas funciones que sigue en suspenso hasta la última función devuelve su valor. La pila se reserva siempre en un orden LIFO, el bloque más recientemente reservada es siempre el siguiente bloque de ser liberados. Esto hace que sea muy simple para realizar un seguimiento de la pila, la liberación de un bloque de la pila no es más que un ajuste de puntero.

Las variables asignados 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 de la pila sólo está limitado por el tamaño de la memoria virtual. Los elementos de la pila no tienen dependencias entre sí y siempre se puede acceder aleatoriamente en cualquier momento. Puede asignar un bloque en cualquier momento y liberarlo en cualquier momento. Esto hace que sea mucho más complejo para realizar un seguimiento de las cuales se asignan partes del montón o libre en un momento dado.

Puede utilizar la pila si se sabe exactamente la cantidad de datos que necesita para asignar antes de tiempo de compilación, y no es demasiado grande. Puede utilizar el montón si no sabe exactamente la cantidad de datos que se necesita en tiempo de ejecución o si es necesario asignar una gran cantidad de datos.

En una situación de múltiples subprocesos cada hilo tendrá su propia pila completamente independientes, sino que compartirá el montón. La pila es hilo específico y el montón es específica de la aplicación. La pila es importante tener en cuenta en el manejo de excepciones e hilo ejecuciones.

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

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

Incluso, se da más detalle aquí y aquí .

Ahora ven a las respuestas de su interrogación .

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

El sistema operativo asigna la pila para cada hilo a nivel de sistema cuando se crea el hilo. Normalmente, el sistema operativo es llamado por el tiempo de ejecución de lenguaje para asignar el montón de la aplicación.

Más se pueden encontrar aquí .

¿Cuál es su alcance?

Aunque ya se había en la parte superior.

"Se puede utilizar la pila si se sabe exactamente la cantidad de datos que necesita para asignar antes de tiempo de compilación, y no es demasiado grande. Se puede utilizar el montón si no sabe exactamente la cantidad de datos que se necesita en tiempo de ejecución o si es necesario asignar una gran cantidad de datos ".

Más se pueden encontrar en aquí .

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

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

Lo hace a uno más rápido?

La asignación de la pila es mucho más rápida, ya que lo único que hace es mover el puntero de la pila. Al usar pools de memoria, puede obtener un rendimiento comparable de la asignación de montón, pero eso conlleva una leve complejidad añadida y sus propios dolores de cabeza.

Además, stack vs. heap 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 desde aquí .




Puesto que algunas respuestas fueron nitpicking, voy a aportar mi ácaros.

Sorprendentemente, nadie ha mencionado que múltiples (es decir, no relacionado con el número de hilos en ejecución de nivel OS) pilas de llamadas se encuentran no sólo en lenguas exóticas (PostScript) o plataformas (Intel Itanium), sino también en fibras , hilos verdes y algunas implementaciones de co-rutinas .

Fibras, hilos verdes y corrutinas son en muchos aspectos similares, lo que conduce a mucha confusión. La diferencia entre las fibras e hilos verdes es que el primero uso multitarea cooperativa, mientras que el último puede presentar ya sea una cooperativa o preventiva (o incluso ambas). Para la distinción entre las fibras y co-rutinas, consulte aquí .

En cualquier caso, el propósito de ambas fibras, hilos verdes y corrutinas está teniendo múltiples funciones de ejecución concurrente, pero no en paralelo (ver este SO pregunta para la distinción) dentro de un solo hilo a nivel de sistema operativo, transfiriendo el control de ida y vuelta el uno del otro de una manera organizada.

Cuando se utilizan fibras, hilos verdes o co-rutinas, que por lo general tiene una pila separada por función. (Técnicamente, no sólo una pila sino todo un contexto de ejecución es por función. Lo más importante, registros de la CPU.) Por cada hilo hay tantas pilas ya que estamos funciones simultáneamente en funcionamiento, y el hilo está conmutando entre la ejecución de cada función de acuerdo a la lógica de su programa. Cuando se ejecuta una función a su fin, su pila se destruye. Por lo tanto, el número y la vida útil de las pilas son dinámicos y no están determinadas por el número de hilos a nivel de sistema operativo!

Tenga en cuenta que dicho " por lo general tiene una pila separada por función". Hay dos estamos stackful y Stackless implementaciones de couroutines. Más notables stackful C ++ implementaciones son Boost.Coroutine y Microsoft PPL 's async/await. (Sin embargo, C ++ 's funciones reanudables (aka " asyncy await"), que se propuso para C ++ 17, es probable que utilice corrutinas Stackless.)

Fibras propuesta de la biblioteca estándar de C ++ está próxima. Además, hay algunos terceros bibliotecas . Hilos verdes son muy populares en lenguajes como Python y Ruby.




Un par de centavos: Creo, que será bueno para dibujar gráfica de memoria y más sencilla:


Flechas - espectáculo donde crecen pila y montón, proceso tamaño de la pila tienen límite, definido en OS, los límites de tamaño de la pila hilo por los parámetros en hilo crean API por lo general. Heap generalmente limitar por proceso tamaño máximo de memoria virtual, para 32 bits 2-4 GB por ejemplo.

Tan simple manera: Montón proceso es general para todos los procesos y roscas interiores, utilizando para la asignación de memoria en caso común con algo como malloc () .

Pila es una memoria rápida para almacenar en caso común punteros y variables de devolución de funciones, procesados ​​como parámetros en llamada de función, las variables de función locales.




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




OK, simple y en pocas palabras, que significan ordenados y no ordenados !

Pila : En los elementos de pila, las cosas se ponen en la parte superior de cada-otra, significa que va a ser más rápido y más eficiente para ser procesado ...

Así que siempre hay un índice para señalar el artículo específico, también procesando va a ser más rápido, no hay relación entre los elementos, así! ...

Montón : Sin orden, procesamiento va a ser más lento y los valores están en mal estado, junto con ningún orden o índice específico ... no son aleatorios y no hay ninguna relación entre ellos ... lo que la ejecución y el uso del tiempo podría variar ...

También se crea la imagen de abajo para mostrar la forma en que pueden verse como:




Una gran cantidad de respuestas que sean correctas, conceptos, pero hay que señalar que una pila es necesaria por el hardware (es decir microprocesador) para permitir que las subrutinas de llamada (CALL en lenguaje ensamblador ..). (POO chicos llamarán métodos )

En la pila de guardar direcciones de retorno y de llamada → empuje / ret → pop es administrado directamente en hardware.

Puede utilizar la pila para pasar parámetros .. incluso si es más lento que el uso de registros (diría un gurú microprocesador o un buen libro 1980 BIOS ...)

  • Sin pila sin microprocesador puede trabajar. (no podemos imaginar un programa, incluso en lenguaje ensamblador, sin subrutinas / funciones)
  • Sin el montón que puede. (Un programa en lenguaje ensamblador puede trabajar sin, como el montón es un concepto OS, como malloc, que es un / OS llamada Lib.

el uso de pila es más rápido tal como:

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



Tengo algo que compartir con ustedes, aunque los puntos principales ya están escritas.

Apilar

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

Montón

  • comparativamente lento acceso a pila.
  • Almacenado en la memoria RAM.
  • las variables dinámicamente creados se almacenan aquí, que más tarde se requiere liberar la memoria asignada después de su uso.
  • Donde almacena la asignación de memoria se realiza, se accede por el puntero siempre.

Nota interesante:

  • Si las llamadas a funciones habían sido almacenados en el montón, sería había resultado en 2 puntos desordenados:
    1. Debido al almacenamiento secuencial en la pila, la ejecución es más rápida. El almacenamiento en montón hubiera resultado en gran consumo de tiempo, resultando de este modo todo el programa para ejecutar más lento.
    2. Si las funciones se almacenan en el montón (almacenamiento desordenado apuntado por el puntero), no habría habido manera de volver a la dirección de la persona que llama de vuelta (que da la pila debido a un almacenamiento secuencial en la memoria).

Evaluaciones se dieron la bienvenida.