¿Por qué el tamaño de la memoria de stack es tan limitado?

Cuando asigna memoria en el montón, el único límite es la memoria RAM libre (o memoria virtual). Hace Gb de memoria.

Entonces, ¿por qué el tamaño de la stack es tan limitado (alrededor de 1 Mb)? ¿Qué razón técnica te impide crear objetos realmente grandes en la stack?

Actualización : Mi intención puede no ser clara, no quiero asignar objetos grandes en la stack y no necesito una stack más grande. Esta pregunta es pura curiosidad.

Mi intuición es la siguiente. La stack no es tan fácil de administrar como el montón. La stack debe almacenarse en ubicaciones de memoria continua. Esto significa que no puede asignar aleatoriamente la stack según sea necesario, pero necesita al menos reservar direcciones virtuales para ese fin. Cuanto mayor sea el tamaño del espacio de direcciones virtuales reservadas, menos hilos se pueden crear.

Por ejemplo, una aplicación de 32 bits generalmente tiene un espacio de direcciones virtuales de 2 GB. Esto significa que si el tamaño de la stack es de 2MB (por defecto en pthreads), entonces puede crear un máximo de 1024 hilos. Esto puede ser pequeño para aplicaciones como servidores web. Aumentando el tamaño de la stack a, digamos, 100MB (es decir, reserva 100MB, pero no necesariamente asigna 100MB a la stack inmediatamente), limitaría el número de subprocesos a aproximadamente 20, lo que puede ser limitante incluso para aplicaciones GUI simples.

Una pregunta interesante es, ¿por qué todavía tenemos este límite en plataformas de 64 bits? No sé la respuesta, pero supongo que la gente ya está acostumbrada a algunas “mejores prácticas de acumulación”: tenga cuidado de asignar grandes objetos en el montón y, si es necesario, boost manualmente el tamaño de la stack. Por lo tanto, a nadie le resultó útil agregar soporte de stack “enorme” en plataformas de 64 bits.

Un aspecto que nadie ha mencionado aún:

Un tamaño de stack limitado es un mecanismo de detección y contención de errores.

En general, el trabajo principal de la stack en C y C ++ es hacer un seguimiento de la stack de llamadas y las variables locales, y si la stack crece fuera de límites, casi siempre es un error en el diseño y / o el comportamiento de la aplicación .

Si se permite que la stack crezca arbitrariamente grande, estos errores (como recursión infinita) se atraparán muy tarde, solo después de que se agoten los recursos del sistema operativo. Esto se evita estableciendo un límite arbitrario para el tamaño de la stack. El tamaño real no es tan importante, aparte de que es lo suficientemente pequeño para evitar la degradación del sistema.

Es solo un tamaño predeterminado. Si necesita más, puede obtener más, generalmente diciéndole al vinculador que asigne espacio extra a la stack.

La desventaja de tener stacks grandes es que si creas muchos hilos, necesitarán una stack cada uno. Si todas las stacks están asignando MB múltiples, pero no lo usan, el espacio se desperdiciará.

Debes encontrar el equilibrio adecuado para tu progtwig.


Algunas personas, como @BJovke, creen que la memoria virtual es esencialmente gratuita. Es cierto que no necesita tener memoria física que respalde toda la memoria virtual. Tienes que ser capaz de al menos dar direcciones a la memoria virtual.

Sin embargo, en una PC típica de 32 bits, el tamaño de la memoria virtual es el mismo que el de la memoria física, porque solo tenemos 32 bits para cualquier dirección, virtual o no.

Como todos los hilos en un proceso comparten el mismo espacio de direcciones, tienen que dividirlo entre ellos. Y después de que el sistema operativo haya tomado su parte, queda “solo” 2-3 GB para una aplicación. Y ese tamaño es el límite tanto para la memoria física como para la virtual, porque simplemente no hay más direcciones.

Por un lado, la stack es continua, por lo que si asigna 12 MB, debe eliminar 12 MB cuando desee ir por debajo de lo que haya creado. También mover objetos alrededor se vuelve mucho más difícil. Aquí hay un ejemplo del mundo real que puede hacer las cosas más fáciles de entender:

Supongamos que está astackndo cajas alrededor de una habitación. Cuál es más fácil de administrar:

  • astackndo cajas de cualquier peso una encima de la otra, pero cuando necesitas conseguir algo en el fondo tienes que deshacer todo tu montón. Si quieres sacar un objeto de la stack y dárselo a otra persona, debes quitar todas las cajas y mover la caja a la stack de la otra persona (solo stack).
  • Colocas todas tus cajas (excepto las cajas realmente pequeñas) en un área especial donde no astacks cosas encima de otras cosas y anotas dónde las pones en un pedazo de papel (un puntero) y colocas el papel sobre la stack. Si necesita entregar la caja a otra persona, simplemente le entrega el papelito de su stack, o simplemente déles una fotocopia del papel y deje el original donde estaba en su stack. (Pila + montón)

Esos dos ejemplos son generalizaciones burdas y hay algunos puntos que son evidentemente incorrectos en la analogía, pero están lo suficientemente cerca como para que, con suerte, te ayuden a ver las ventajas en ambos casos.

Piense en la stack en el orden de casi muy lejos. Los registros están cerca de la CPU (rápido), la stack está un poco más lejos (pero todavía está relativamente cerca) y el montón está lejos (acceso lento).

La stack vive en el montón, por supuesto, pero aún así, dado que se usa de forma continua, probablemente nunca abandone la memoria caché de la CPU, por lo que es más rápido que solo el acceso promedio al montón. Esta es una razón para mantener la stack de un tamaño razonable; para mantenerlo en la memoria caché tanto como sea posible. La asignación de objetos de gran stack (posiblemente redimensionando automáticamente la stack a medida que se producen desbordamientos) va en contra de este principio.

Entonces, es un buen paradigma para el rendimiento, no solo un residuo de los viejos tiempos.

Muchas de las cosas para las que crees que necesitas una gran stack, se pueden hacer de otra forma.

Los “Algoritmos” de Sedgewick tienen un par de buenos ejemplos de “eliminar” la recursión de algoritmos recursivos como QuickSort, al reemplazar la recursión con iteración. En realidad, el algoritmo sigue siendo recursivo, y todavía existe como stack, pero usted asigna la stack de clasificación en el montón, en lugar de usar la stack de tiempo de ejecución.

(Estoy a favor de la segunda edición, con algoritmos dados en Pascal. Se puede haber usado por ocho dólares).

Otra forma de verlo es que si crees que necesitas una gran stack, tu código es ineficiente. Hay una forma mejor de usar menos stack.

No creo que haya ninguna razón técnica, pero sería una aplicación extraña que acaba de crear solo un enorme súper objeto en la stack. Los objetos de stack carecen de flexibilidad que se vuelve más problemática a medida que aumenta el tamaño: no se puede volver sin destruirlos y no se pueden poner en cola con otros hilos.