2012-01-02 15 views
17

¿Alguien puede explicar claramente, en términos de C, C++ y Java. Lo que sucede con la pila y todo lo que sucede en el montón y cuándo se realiza la asignación.¿Qué se almacena en el montón y qué se almacena en la pila?

Por lo que yo sé,

Todas las variables locales si primitivas, punteros o variables de referencia por cada llamada a la función se encuentran en un nuevo marco de pila.

y todo lo creado con new o malloc va en montón.

Estoy confundido acerca de algunas cosas.

¿Las referencias/primitivas que son miembros de un objeto creado en el montón también se almacenan en el montón?

y los miembros locales de un método que se crean recursivamente en cada cuadro. ¿Están todos en la pila? En caso afirmativo, ¿esa memoria de la pila está asignada en tiempo de ejecución? también para literales, ¿son parte del segmento de código? y qué hay de globales en C, estáticos en C++/Java y estáticos en C.

+3

Las variables locales no están necesariamente en el marco de la pila; pueden existir solo en los registros, o incluso pueden optimizarse completamente. –

+2

Idealmente, estos son todos los detalles de implementación. Qué implementaciones estás comparando – trashgod

+0

implementaciones estándar Implementación de JDK que implementa Sun/Oracle. y para C/C++ estoy hablando de gcc. –

Respuesta

33

Estructura de un programa en la memoria

La siguiente es la estructura básica de cualquier programa cuando se carga en la memoria.

+--------------------------+ 
|       | 
|  command line  | 
|  arguments   | 
| (argc and argv[])  | 
|       | 
+--------------------------+ 
| Stack     | 
| (grows-downwards)  | 
|       | 
|       | 
|       | 
|   F R E E   | 
|  S P A C E   | 
|       | 
|       | 
|       | 
|       | 
|  (grows upwards) Heap | 
+--------------------------+ 
|       | 
| Initialized data  | 
|   segment   | 
|       | 
+--------------------------+ 
|       | 
|  Initialized to  | 
|  Zero (BSS)  | 
|       | 
+--------------------------+ 
|       | 
|  Program Code  | 
|       | 
+--------------------------+ 

Algunos puntos a tener en cuenta:

  • segmento de datos
    • segmento de datos inicializados (inicializado a inicializadores explícitos por los programadores)
    • segmento de datos sin inicializar (inicializado a segmento de datos cero - NBS [ Bloquear el inicio con el símbolo])
  • Segmento de código
  • Stack y Heap áreas

segmento de datos

El segmento de datos contiene los datos globales y estáticos que se ha inicializado explícitamente por los usuarios que contienen los valores inicializado.

La otra parte del segmento de datos se llama BSS (debido a que los sistemas anteriores de IBM tenían ese segmento inicializado en cero). Es la parte de la memoria donde el SO inicializa el bloque de memoria a ceros. Así es como los datos globales no inicializados y estáticos obtienen el valor predeterminado como cero. Esta área es fija y tiene un tamaño estático.

El área de datos se divide en dos áreas según la inicialización explícita, ya que las variables que se van a inicializar se pueden inicializar una a una. Sin embargo, las variables que no se inicializan no se deben inicializar explícitamente con 0 de uno en uno. En lugar de eso, el trabajo de inicialización de la variable se deja al sistema operativo.Esta inicialización masiva puede reducir en gran medida el tiempo requerido para cargar el archivo ejecutable.

Principalmente el diseño del segmento de datos está bajo el control del sistema operativo subyacente, aún algunos cargadores dan control parcial a los usuarios. Esta información puede ser útil en aplicaciones tales como sistemas integrados.

Esta área se puede direccionar y acceder usando punteros del código. Las variables automáticas tienen una sobrecarga al inicializar las variables cada vez que son necesarias y se requiere código para hacer esa inicialización. Sin embargo, las variables en el área de datos no tienen una sobrecarga de tiempo de ejecución debido a que la inicialización se realiza solo una vez y también durante el tiempo de carga.

segmento Código

código El programa es el área de código en el código ejecutable está disponible para su ejecución. Esta área también es de tamaño fijo. Solo se puede acceder a estos punteros de función y no a otros punteros de datos. Otra información importante a tener en cuenta aquí es que el sistema puede considerar esta área como área de memoria de solo lectura y cualquier intento de escribir en esta área conduce a un comportamiento indefinido.

Las cadenas constantes se pueden colocar en el código o en el área de datos y eso depende de la implementación.

El intento de escribir en el área de código conduce a un comportamiento indefinido. Por ejemplo (voy a dar solo ejemplos basados ​​en C) el siguiente código puede ocasionar un error en el tiempo de ejecución o incluso bloquear el sistema.

int main() 
{ 
    static int i; 
    strcpy((char *)main,"something"); 
    printf("%s",main); 
    if(i++==0) 
    main(); 
} 

pila y montón áreas

Para la ejecución, el programa utiliza dos partes principales, la pila y montón. Los marcos de pila se crean en pila para funciones y en pila para asignación de memoria dinámica. La pila y el montón son áreas no inicializadas. Por lo tanto, lo que sea que esté allí en la memoria se convierte en el valor inicial (basura) para los objetos creados en ese espacio.

Veamos un programa de ejemplo para mostrar las variables que se almacenan en donde,

int initToZero1; 
static float initToZero2; 
FILE * initToZero3; 
// all are stored in initialized to zero segment(BSS) 

double intitialized1 = 20.0; 
// stored in initialized data segment 

int main() 
{ 
    size_t (*fp)(const char *) = strlen; 
    // fp is an auto variable that is allocated in stack 
    // but it points to code area where code of strlen() is stored 

    char *dynamic = (char *)malloc(100); 
    // dynamic memory allocation, done in heap 

    int stringLength; 
    // this is an auto variable that is allocated in stack 

    static int initToZero4; 
    // stored in BSS 

    static int initialized2 = 10; 
    // stored in initialized data segment 

    strcpy(dynamic,”something”);  
    // function call, uses stack 

    stringLength = fp(dynamic); 
    // again a function call 
} 

O considérese un ejemplo todavía más complejo,

// command line arguments may be stored in a separate area 
int main(int numOfArgs, char *arguments[]) 
{ 
    static int i; 
    // stored in BSS 

    int (*fp)(int,char **) = main; 
    // points to code segment 

    static char *str[] = {"thisFileName","arg1", "arg2",0}; 
    // stored in initialized data segment 

    while(*arguments) 
     printf("\n %s",*arguments++); 

    if(!i++) 
     fp(3,str); 
} 

Espero que esto ayude!

+0

cuando mmap algo en el espacio del programa, ¿dónde está mapeado? +1 para una explicación tan agradable –

+1

@ Mr.32 Gracias por el +1. No estoy seguro de a qué 'mmap()' pertenece en el diseño anterior. Es una buena pregunta. ¡Estoy tratando de explorar lo mismo! OTOH, estaba tratando de mantener la descripción genérica para todos los lenguajes de programación mencionados anteriormente. ¡Debería haber cubierto las variables 'register' y' extern' también! –

+0

¡WOW definitivamente ayuda! –

5

En C/C++: Las variables locales se asignan en el marco de pila actual (que pertenece a la función actual). Si asignas estáticamente un objeto, el objeto completo se asigna a la pila, incluidas todas sus variables miembro. Cuando se utiliza recursividad, con cada llamada de función se crea un nuevo marco de pila, y todas las variables locales se asignan en la pila. La pila generalmente tiene un tamaño fijo que, y este valor, generalmente se escribe en el encabezado binario ejecutable durante la compilación/vinculación. Sin embargo, esto es muy específico del sistema operativo y plataforma, algunos sistemas operativos pueden hacer crecer la pila de forma dinámica cuando sea necesario. Debido a que el tamaño de la pila generalmente es limitado, puede quedarse sin pila cuando usa recursión profunda o, a veces, incluso sin recursión cuando asigna de forma estática objetos grandes.

El montón generalmente se toma como un espacio ilimitado (solo limitado por la memoria física/virtual disponible), y puede asignar objetos en el montón usando malloc/new (y otras funciones de asignación de montón). Cuando se crea un objeto en el montón, todas sus variables miembro se crean dentro de él. Debería ver un objeto como un área continua de memoria (esta área contiene variables miembro y un puntero a una tabla de método virtual), sin importar dónde esté asignado.

Literales, constantes y otras cosas "fijas" generalmente se compilan/vinculan en el binario como otro segmento, por lo que no es realmente el segmento de código. Por lo general, no puedes asignar o liberar nada de este segmento en tiempo de ejecución. Sin embargo, esto también es específico de la plataforma, podría funcionar de manera diferente en diferentes plataformas (por ejemplo, el código iOS Obj-C tiene muchas referencias constantes insertadas directamente en el segmento de código, entre funciones).

2

En Java, las variables locales se pueden asignar don la pila (a menos optimizado de distancia)

Primitivas y referencias en un objeto son en el montón (como el objeto está en el montón)

Una pila es preasignado cuando se crea el hilo. No usa espacio de montón. (Sin embargo, la creación de un hilo da como resultado la creación de un Buffer de asignación local de subprocesos que disminuye un poco la memoria libre)

Se agregan literales únicos al heap. Los literales primitivos pueden estar en algún lugar del código (si no están optimizados). Si un campo es estático o no, no hay diferencia.

+0

Si la variable local es un objeto, solo el puntero se almacena en la pila. El montón en Java es generalmente más organizado. Una implementación de JVM mantiene una generación "joven" de objetos (por ejemplo, de corta duración), reflejando de alguna manera el concepto de pila, pero con administración. A diferencia de C++, a veces debe clonar objetos apilados. –

+1

@JoopEggen Desde Java 6 Update 14, Oracle (Sun) JVM también podrá realizar análisis de escape e impedir que se creen objetos en el montón. Ver http://www.oracle.com/technetwork/java/javase/6u14-137039.html –

+0

El análisis de escape funciona bien a veces, sin embargo, muchas situaciones en las que crees que debería funcionar, no. ;) –

4

En C y C++, al menos, esto es todo específico de la implementación. Los estándares no mencionan "stack" o "heap".

1

Para responder a la parte de su pregunta sobre C++ pila y pila:

debería decir en primer lugar que un objeto creado sin nueva se almacena como una unidad contigua en la pila o si global en algún segmento mundial (plataforma específica)

Para un objeto que se crea usando new en el montón, sus variables miembro se almacenan como un bloque contiguo de memoria en el montón. Este es el caso de las variables miembro que son primitivas y objetos incrustados. En el caso de variables miembro que son punteros y variables de miembro de tipo de referencia, se almacena un valor de puntero primitivo dentro del objeto. A qué apunta ese valor se puede almacenar en cualquier lugar (montón, pila, global). Todo es posible.

En cuanto a las variables locales dentro de los métodos de un objeto, se almacenan en la pila, no dentro del espacio contiguo de los objetos en el montón. La pila generalmente se crea de un tamaño fijo en tiempo de ejecución. Hay uno por hilo Las variables locales pueden incluso no consumir espacio en la pila, ya que pueden optimizarse (como dijo Paul). El punto principal es que no están en el montón simplemente porque son funciones miembro de un objeto en el montón. Si son variables locales de un tipo de puntero, podrían almacenarse en la pila y apuntar a algo en el montón o en la pila.

2

Section 3.5 de Java Virtual Machine Specification describe las áreas de datos de tiempo de ejecución (las pilas y el montón).

Ni los estándares de lenguaje C ni C++ especifican si algo debe almacenarse en una pila o en un montón. Solo definen la duración del objeto, la visibilidad y la modificabilidad; Depende de la implementación asignar esos requisitos al diseño de memoria de una plataforma en particular.

Típicamente, cualquier cosa asignado con los *alloc funciones reside en el montón, mientras auto variables y parámetros de la función residen en una pila. Los literales de cadena pueden vivir "en otro lugar" (deben asignarse y ser visibles a lo largo de la vida del programa, pero intentar modificarlos no está definido); algunas plataformas usan un segmento de memoria separado de solo lectura para almacenarlas.

Solo recuerde que hay algunas plataformas verdaderamente extrañas que pueden no ajustarse al modelo común de pila acumulada.

Cuestiones relacionadas