Hay dos límites de memoria diferentes. El límite de memoria virtual y el límite de memoria física.
Memoria virtual
La memoria virtual está limitada por el tamaño y el diseño del espacio de direcciones disponible. Por lo general, al principio es el código ejecutable y los datos estáticos y pasados que crecen el montón, mientras que al final es el área reservada por el núcleo, antes que las bibliotecas compartidas y la pila (que en la mayoría de las plataformas disminuye). Eso le da al montón y a la pila espacio libre para crecer, las otras áreas se conocen y se reparan al inicio del proceso.
La memoria virtual libre no se marca inicialmente como utilizable, pero se marca como tal durante la asignación. Si bien el almacenamiento dinámico puede crecer a toda la memoria disponible, la mayoría de los sistemas no crecen automáticamente las pilas. El límite predeterminado de IIRC para la pila es 8MiB en Linux y 1MiB en Windows y se puede cambiar en ambos sistemas. La memoria virtual también contiene cualquier archivo y hardware mapeados en memoria.
Una razón por la cual la pila no puede crecer automáticamente (arbitrariamente) es que los programas multiproceso necesitan una pila separada para cada subproceso, por lo que eventualmente se interpondrán en el camino del otro.
En las plataformas de 32 bits, la cantidad total de memoria virtual es de 4 GiB, tanto Linux como Windows normalmente reservan los últimos 1 GiB para el núcleo, lo que le brinda un máximo de 3 GB de espacio de direcciones. Hay una versión especial de Linux que no reserva nada que le brinde 4GiB completos. Es útil para el raro caso de grandes bases de datos donde el último 1GiB ahorra el día, pero para el uso regular es un poco más lento debido a las recargas adicionales de la tabla de páginas.
En las plataformas de 64 bits, la memoria virtual es 64EiB y no tiene que pensarlo.
Memoria física
La memoria física generalmente solo es asignada por el sistema operativo cuando el proceso necesita acceder a ella. La cantidad de memoria física que utiliza un proceso es un número muy difuso, ya que parte de la memoria se comparte entre procesos (el código, las bibliotecas compartidas y cualquier otro archivo asignado), los datos de los archivos se cargan en la memoria a pedido y se descartan cuando hay escasez de memoria y La memoria "anónima" (la que no está respaldada por archivos) puede intercambiarse.
En Linux, lo que sucede cuando se queda sin memoria física depende de la vm.overcommit_memory
configuración del sistema. El valor predeterminado es comprometerse de más. Cuando le pide al sistema que asigne memoria, le da algo, pero solo asigna la memoria virtual. Cuando realmente acceda a la memoria, intentará obtener algo de memoria física para usar, descartando datos que se puedan releer o intercambiando cosas según sea necesario. Si descubre que no puede liberar nada, simplemente eliminará el proceso de la existencia (no hay forma de reaccionar, porque esa reacción podría requerir más memoria y eso conduciría a un bucle sin fin).
Así es como mueren los procesos en Android (que también es Linux). La lógica se mejoró con la lógica de qué proceso eliminar de la existencia en función de lo que está haciendo el proceso y la antigüedad que tiene. Los procesos de Android simplemente dejan de hacer cualquier cosa, pero se sientan en segundo plano y el "asesino sin memoria" los matará cuando necesite memoria para los nuevos.