increase - limit heap size java
¿Por qué la JVM(Sun) tiene un límite superior fijo para el uso de memoria(-Xmx)? (9)
¿Por qué este límite fijo existe? ¿Por qué la JVM no asigna memoria según sea necesario, como lo hacen los programas nativos en la mayoría de los sistemas operativos?
La razón NO es que el GC necesite saber de antemano cuál es el tamaño máximo de almacenamiento dinámico. La JVM es claramente capaz de expandir su montón ... hasta el máximo ... y estoy seguro de que sería un cambio relativamente pequeño eliminar ese máximo. (Después de todo, otras implementaciones de Java hacen esto.) Y también sería posible tener una manera simple de decir "usar tanta memoria como desee" para la JVM.
Estoy seguro de que el verdadero motivo es proteger el sistema operativo host contra los efectos de aplicaciones Java defectuosas que utilizan toda la memoria disponible. Correr con un montón sin límites es potencialmente peligroso.
Básicamente, muchos sistemas operativos (por ejemplo, Windows, Linux) sufren una grave degradación del rendimiento si alguna aplicación intenta usar toda la memoria disponible. En Linux, por ejemplo, el sistema puede funcionar mal, lo que hace que todo en el sistema funcione increíblemente lento. En el peor de los casos, el sistema no podrá iniciar nuevos procesos, y los procesos existentes pueden comenzar a fallar cuando el sistema operativo rechaza sus solicitudes (legítimas) de más memoria. A menudo, la única opción es reiniciar.
Si la JVM se ejecutaba de manera predeterminada con un montón ilimitado, cada vez que alguien ejecutaba un programa Java con una fuga de almacenamiento ... o simplemente intentaba usar demasiada memoria ... se arriesgaban a derribar todo el sistema operativo.
En resumen, tener un enlace de montón predeterminado es algo bueno porque:
- protege la salud de su sistema,
- alienta a los desarrolladores / usuarios a pensar sobre el uso de la memoria por aplicaciones "hambrientas", y
- potencialmente permite optimizaciones de GC. (Como sugieren otras respuestas: es plausible, pero no puedo confirmarlo).
EDITAR
En respuesta a los comentarios:
Realmente no importa por qué las JVM de Sun viven dentro de un montón limitado, donde otras aplicaciones no lo hacen. Lo hacen, y las ventajas de hacerlo son (IMO) claras. Quizás una pregunta más interesante es por qué otros lenguajes administrados no ponen un límite en sus montones por defecto.
Los
-Xmx
y-Xmx
son cualitativamente diferentes. En el primer caso, la JVM tiene pleno conocimiento de los límites bajo los cuales se está ejecutando y tiene la oportunidad de administrar su uso de memoria en consecuencia. En este último caso, lo primero que sabe una aplicación C típica es cuando falla una llamadamalloc
. La respuesta típica es salir con un código de error (si el programa verifica el resultadomalloc
), o morir con un error de segmentación. De acuerdo, una aplicación C podría, en teoría, hacer un seguimiento de la cantidad de memoria que ha utilizado e intentar responder a una inminente crisis de memoria. Pero sería un trabajo duro.La otra cosa que es diferente acerca de las aplicaciones Java y C / C ++ es que las primeras tienden a ser más complicadas y más largas. En la práctica, esto significa que las aplicaciones Java son más propensas a sufrir fugas lentas. En el caso de C / C ++, el hecho de que la gestión de la memoria sea más difícil significa que los desarrolladores no intentan crear aplicaciones únicas de esa complejidad. Por el contrario, es más probable que construyan (diga) un servicio complejo haciendo que un oyente procese un fork de procesos secundarios para hacer cosas ... y luego salga. Esto naturalmente mitiga el efecto de las pérdidas de memoria en el proceso del niño.
La idea de una JVM que responda "de manera adaptativa" a las solicitudes del sistema operativo para devolver la memoria es interesante. Pero hay un GRAN problema. Para devolver un segmento de memoria, la JVM primero tiene que eliminar cualquier objeto alcanzable en el segmento. Normalmente, eso significa ejecutar el recolector de basura. Pero ejecutar el recolector de basura es lo último que desea hacer si el sistema está en una crisis de memoria ... porque está prácticamente garantizado que generará un estallido de paginación de memoria virtual.
En el espíritu de la pregunta Java: ¿Por qué existe MaxPermSize? , Me gustaría preguntar por qué Sun JVM usa un límite superior fijo para el tamaño de su grupo de asignación de memoria.
El valor predeterminado es 1/4 de su RAM física (con límite superior e inferior); como consecuencia, si tiene una aplicación que requiere mucha memoria, tiene que cambiar manualmente el límite (parámetro -Xmx), o su aplicación funcionará mal, incluso se bloqueará con un OutOfMemoryError.
¿Por qué este límite fijo existe? ¿Por qué la JVM no asigna memoria según sea necesario, como lo hacen los programas nativos en la mayoría de los sistemas operativos?
Esto resolvería toda una clase de problemas comunes con el software Java (solo google para ver cuántos consejos hay en la red para resolver problemas al establecer -Xmx).
Editar:
Algunas respuestas señalan que esto protegerá al resto del sistema de un programa Java con una fuga de memoria de escape; sin el límite, esto reduciría todo el sistema al agotar toda la memoria. Esto es cierto, sin embargo, es igualmente cierto para cualquier otro programa, y los sistemas operativos modernos ya le permiten limitar la memoria máxima para un programa (Linux ulimit, Windows "Objetos de trabajo"). Así que esto realmente no responde a la pregunta, que es "¿Por qué la JVM lo hace de manera diferente a la mayoría de los demás programas / entornos de tiempo de ejecución?".
¿No tendría más sentido separar el límite superior que desencadena GC y el máximo que se puede asignar? Una vez que la memoria asignada llega al límite superior, GC puede iniciar y liberar algo de memoria al grupo libre. algo así como cómo limpio mi escritorio que comparto con mi compañero de trabajo. Tengo un escritorio grande, y mi umbral de la cantidad de basura que puedo tolerar en la mesa es mucho menor que el tamaño de mi escritorio. No necesito llenar cada pulgada disponible antes de juntar basura.
También podría devolver parte del espacio de escritorio que estaba usando a mi compañero de trabajo, que está compartiendo mi escritorio ... Entiendo que los jvms no devuelven la memoria al sistema una vez que se la asignaron a sí mismos, pero no tiene por qué ser así no?
Asigna memoria según sea necesario, hasta -Xmx;)
Una razón por la que puedo pensar es que una vez que la JVM asigna una cantidad de memoria para su pila, nunca la dejará ir. Entonces, si su montón no tiene límite superior, la JVM simplemente puede tomar toda la memoria libre en el sistema y luego nunca la suelte.
El límite superior también le dice a la JVM cuando necesita hacer una recolección de basura completa. Si su aplicación aún se encuentra por debajo del límite superior, la JVM pospondrá la recolección de elementos no utilizados y permitirá que crezca la huella de memoria de su aplicación.
Los programas nativos también pueden morir debido a errores de memoria insuficiente, ya que las aplicaciones nativas también tienen un límite de memoria: la memoria disponible en el sistema, la memoria que ya tienen otras aplicaciones.
La JVM también necesita un bloque contiguo de memoria del sistema para que la recolección de basura se realice de manera eficiente.
EDITAR
Reclamo de memoria contigua o here
Aparentemente, la JVM dejará pasar algo de memoria, pero es rare con la configuración predeterminada.
Creo que el límite superior para la memoria está relacionado con el hecho de que JVM es una máquina virtual.
Como cualquier máquina física tiene una cantidad dada (fija) de RAM, la máquina virtual tiene una.
El tamaño máximo hace que la JVM sea más fácil de administrar por el sistema operativo y asegura algunas ganancias de rendimiento (menos intercambio).
Sun ''JVM también funciona en una arquitectura de hardware bastante limitada (sistemas ARM integrados) y allí la administración de recursos es crucial.
Creo que parte de esto tiene que ver con la implementación del Garbage Collector (GC). El GC generalmente es flojo, lo que significa que solo comenzará a tratar realmente de recuperar memoria internamente cuando el montón esté en su tamaño máximo. Si no estableció un límite superior, el tiempo de ejecución felizmente continuará inflándose hasta que use cada bit de memoria disponible en su sistema.
Esto se debe a que, desde la perspectiva de la aplicación, es más eficiente tomar más recursos que esforzarse por utilizar los recursos que ya tiene para su plena utilización. Esto tiende a tener sentido para muchos (si no la mayoría) usos de Java, que es una configuración de servidor donde la aplicación es literalmente lo único que importa en el servidor. Tiende a ser ligeramente menos ideal cuando intenta implementar un cliente en Java, que se ejecutará entre docenas de otras aplicaciones al mismo tiempo.
Recuerde que con programas nativos, el programador normalmente solicita pero también limpia de manera explícita los recursos. Eso no es típicamente cierto en entornos que realizan administración automática de memoria.
Hm, intentaré resumir las respuestas hasta ahora.
No hay ninguna razón técnica por la cual la JVM necesite tener un límite estricto para su tamaño de almacenamiento dinámico. Podría haber sido implementado sin una, y de hecho muchos otros lenguajes dinámicos no tienen esto.
Por lo tanto, dar a la JVM un límite de tamaño de pila fue simplemente una decisión de diseño de los implementadores. En segundo lugar, adivinar por qué se hizo esto es un poco difícil, y puede que no haya una sola razón. La razón más probable es que ayuda a proteger un sistema de un programa Java con una pérdida de memoria, que de lo contrario podría agotar toda la memoria RAM y causar que otras aplicaciones se bloqueen o el sistema se agite.
Sun pudo haber omitido la función y simplemente le dijo a la gente que utilizara los mecanismos de limitación de recursos nativos del sistema operativo, pero probablemente querían tener siempre un límite, por lo que lo implementaron ellos mismos. En cualquier caso, la JVM debe conocer dicho límite (para adaptar su estrategia de GC), por lo que el uso de un mecanismo nativo del sistema operativo no habría ahorrado mucho esfuerzo de programación.
Además, hay una razón por la cual dicho límite incorporado es más importante para la JVM que para un programa "normal" sin GC (como un programa C / C ++):
A diferencia de un programa con gestión de memoria manual, un programa que utiliza GC realmente no tiene un requisito de memoria bien definido, incluso con datos de entrada fijos. Solo tiene un requisito mínimo, es decir, la suma de los tamaños de todos los objetos que realmente están en vivo (alcanzables) en un punto dado en el tiempo. Sin embargo, en la práctica, un programa necesitará memoria adicional para contener objetos muertos, pero aún no GCed, porque el GC no puede recoger todos los objetos de inmediato, ya que eso causaría demasiada sobrecarga de GC. Por lo tanto, el GC solo entra en acción de vez en cuando y, por lo tanto, se necesita un "respiro" en el montón, donde los objetos muertos pueden esperar al GC.
Esto significa que la memoria requerida para un programa que utiliza GC es realmente un compromiso entre guardar la memoria y tener un buen flujo (permitiendo que el GC funcione con menos frecuencia). Por lo tanto, en algunos casos, puede tener sentido establecer el límite de almacenamiento dinámico más bajo que el que usaría la JVM si fuera posible, por lo tanto, guarde la RAM a expensas del rendimiento. Para hacer esto, debe haber una manera de establecer un límite de montón.
Se debe al diseño de la JVM. Otras JVM (como la de Microsoft y algunas de IBM) pueden usar toda la memoria disponible en el sistema si es necesario, sin un límite arbitrario.
Creo que permite optimizaciones GC.
Solo uno de los muchos lugares donde Java es realmente bueno para los servidores y realmente malo para los escritorios. Impresionante que pueda darle una cantidad máxima y mínima de memoria en un servidor, la JVM perdería una gran cantidad de energía si creciera hasta que comenzara la búsqueda en el disco. Sin embargo, esto hace que sea realmente terrible para una aplicación de escritorio que se ejecuta en muchas máquinas diferentes :)
Una respuesta que ninguno de los anteriores dio es que JVM usa grupos de memoria tanto de montón como de otro tipo. Al poner un límite superior en el montón, no solo se define la cantidad de memoria disponible para los grupos de memoria de montón, sino que también se define cuánta memoria hay disponible para usos que NO SEAN HECHOS. Supongo que la JVM podría asignar no-heap en la parte superior de la memoria virtual y el montón en la parte inferior de la memoria virtual y crecer tanto el uno hacia el otro.
La memoria que no es de montón incluye los archivos DLL o SO que componen la JVM y cualquier código nativo que se utilice, así como código compilado de Java, pilas de subprocesos, objetos nativos, PermGen (metadatos sobre clases compiladas), entre otros usos. He visto bloquearse los programas de Java porque se le había dado tanta memoria al montón que la aplicación se quedó sin memoria. Aquí es donde aprendí que puede ser importante reservar memoria para usos no acumulativos al no configurar el montón para que sea demasiado grande.
Esto hace una diferencia mucho mayor en un mundo de 32 bits donde una aplicación a menudo tiene solo 2 GB de espacio de direcciones virtuales que en un mundo de 64 bits, por supuesto.