Cuando asigna memoria en el montón, el único límite es RAM libre (o memoria virtual). Genera Gb de memoria.
Entonces, ¿por qué el tamaño de la pila es tan limitado (alrededor de 1 Mb)? ¿Qué razón técnica le impide crear objetos realmente grandes en la pila?
Actualización : es posible que mi intención no sea clara, no quiero asignar objetos grandes en la pila y no necesito una pila más grande. Esta pregunta es pura curiosidad.
c++
memory-management
ondulado
fuente
fuente
Respuestas:
Mi intuición es la siguiente. La pila no es tan fácil de administrar como el montón. La pila debe almacenarse en ubicaciones de memoria continua. Esto significa que no puede asignar aleatoriamente la pila según sea necesario, pero necesita al menos reservar direcciones virtuales para ese propósito. Cuanto mayor sea el tamaño del espacio de direcciones virtuales reservado, menos subprocesos puede crear.
Por ejemplo, una aplicación de 32 bits generalmente tiene un espacio de direcciones virtual de 2 GB. Esto significa que si el tamaño de la pila es de 2 MB (por defecto en pthreads), puede crear un máximo de 1024 hilos. Esto puede ser pequeño para aplicaciones como servidores web. Aumentar el tamaño de la pila a, digamos, 100 MB (es decir, reserva 100 MB, pero no necesariamente asigna 100 MB a la pila 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 pila": tenga cuidado de asignar objetos enormes en la pila y, si es necesario, aumente manualmente el tamaño de la pila. Por lo tanto, a nadie le resultó útil agregar soporte de pila "enorme" en plataformas de 64 bits.
fuente
Un aspecto que nadie ha mencionado todavía:
Un tamaño de pila limitado es un mecanismo de detección y contención de errores.
Generalmente, el trabajo principal de la pila en C y C ++ es realizar un seguimiento de la pila de llamadas y las variables locales, y si la pila crece fuera de los límites, casi siempre es un error en el diseño y / o el comportamiento de la aplicación. .
Si se permitiera que la pila creciera arbitrariamente, estos errores (como la recursividad infinita) se detectarían 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 pila. El tamaño real no es tan importante, aparte de que es lo suficientemente pequeño como para evitar la degradación del sistema.
fuente
std::unique_ptr
para escribir un destructor (y no depender del puntero inteligente)).Es solo un tamaño predeterminado. Si necesita más, puede obtener más, generalmente diciéndole al vinculador que asigne espacio de pila adicional.
La desventaja de tener pilas grandes es que si crea muchos subprocesos, necesitarán una pila cada uno. Si todas las pilas asignan varios MB, pero no los utilizan, se desperdiciará espacio.
Tienes que encontrar el equilibrio adecuado para tu programa.
Algunas personas, como @BJovke, creen que la memoria virtual es esencialmente gratuita. Es cierto que no es necesario tener una memoria física que respalde toda la memoria virtual. Tienes que poder 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 tamaño de la memoria física, porque solo tenemos 32 bits para cualquier dirección, virtual o no.
Debido a que todos los subprocesos de un proceso comparten el mismo espacio de direcciones, deben dividirlo entre ellos. Y después de que el sistema operativo ha tomado su parte, quedan "sólo" 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.
fuente
Por un lado, la pila es continua, por lo que si asigna 12 MB, debe eliminar 12 MB cuando desee ir por debajo de lo que creó. Además, mover objetos se vuelve mucho más difícil. A continuación, se muestra un ejemplo del mundo real que puede facilitar la comprensión de las cosas:
Digamos que está apilando cajas alrededor de una habitación. Cuál es más fácil de administrar:
Esos dos ejemplos son generalizaciones burdas y hay algunos puntos que son descaradamente erróneos en la analogía, pero está lo suficientemente cerca como para ayudarlo a ver las ventajas en ambos casos.
fuente
Piense en la pila en el orden de cerca a lejos. Los registros están cerca de la CPU (rápido), la pila está un poco más lejos (pero aún relativamente cerca) y el montón está lejos (acceso lento).
La pila vive en el montón, por supuesto, pero aún así, dado que se usa continuamente, probablemente nunca abandone la (s) caché (s) de la CPU, lo que lo hace más rápido que el acceso al montón promedio. Ésta es una razón para mantener la pila de un tamaño razonable; para mantenerlo en caché tanto como sea posible. Asignar objetos de pila grande (posiblemente cambiar el tamaño de la pila automáticamente a medida que se desbordan) va en contra de este principio.
Así que es un buen paradigma para el rendimiento, no solo un remanente de los viejos tiempos.
fuente
Muchas de las cosas para las que crees que necesitas una gran pila se pueden hacer de otra manera.
Los "Algoritmos" de Sedgewick tienen un par de buenos ejemplos de "eliminar" la recursividad de algoritmos recursivos como QuickSort, reemplazando la recursividad con iteración. En realidad, el algoritmo sigue siendo recursivo y todavía hay una pila, pero usted asigna la pila de clasificación en el montón, en lugar de utilizar la pila en tiempo de ejecución.
(Estoy a favor de la segunda edición, con algoritmos dados en Pascal. Se puede usar por ocho dólares).
Otra forma de verlo es que si cree que necesita una pila grande, su código es ineficiente. Hay una forma mejor que usa menos pila.
fuente
No creo que haya ninguna razón técnica, pero sería una aplicación extraña que acaba de crear un gran superobjeto en la pila. Los objetos apilados carecen de flexibilidad, lo que se vuelve más problemático con el aumento de tamaño: no puede regresar sin destruirlos y no puede ponerlos en cola en otros subprocesos.
fuente
int main() { char buffer[1048576]; }
Es un problema de novato muy común. Seguro que hay una solución alternativa fácil, pero ¿por qué deberíamos tener que solucionar el tamaño de la pila?