¿Por qué se fija el tamaño de almacenamiento dinámico en las JVM?

20

¿Alguien puede explicarme por qué las JVM (no verifiqué demasiadas, pero nunca he visto una que no lo haya hecho de esa manera) necesitan ejecutarse en un tamaño de almacenamiento dinámico fijo? Sé que es más fácil de implementar en un montón contiguo simple, pero Sun JVM ahora tiene más de una década, por lo que esperaría que hayan tenido tiempo para mejorar esto.

La necesidad de definir el tamaño máximo de memoria de su programa en el momento del inicio parece una cosa de los años 60, y luego están las malas interacciones con la gestión de memoria virtual del sistema operativo (GC recuperando datos intercambiados, incapacidad para determinar cuánta memoria es el proceso de Java Realmente usando desde el lado del sistema operativo, se desperdician grandes cantidades de espacio de VM (lo sé, no te importan tus elegantes máquinas de 48 bits ...)). También supongo que los diversos intentos tristes de construir pequeños sistemas operativos dentro de la JVM (servidores de aplicaciones EE, OSGi) son al menos parcialmente responsables de esta circunstancia, porque ejecutar múltiples procesos Java en un sistema invariablemente conduce a recursos desperdiciados porque tiene que dar a cada uno de ellos la memoria que podría tener que usar en el pico.

Sorprendentemente, Google no generó las tormentas de indignación sobre esto que yo esperaría, pero pueden haber sido enterrados bajo los millones de personas que se enteraron del tamaño fijo del montón y simplemente lo aceptaron de hecho.

themel
fuente
El diseño de los servidores de aplicaciones EE tendría mucho sentido incluso sin esta "circunstancia", ya que la JVM en sí misma necesita algo de espacio, y cambiar entre hilos es más barato que cambiar entre procesos, esa es una de las cosas que hicieron grande a Java a finales de los 90.
Michael Borgwardt
Esto es muy amable, y me pregunto cuánto pensamiento has puesto en ello. Por ejemplo, ¿cómo cambiaría la interacción GC / intercambio si no tuviera un límite de almacenamiento dinámico? ¿Cómo se desperdicia el espacio de VM? Obtiene su espacio de 2 / 3Gb, lo use o no, y si está presionando los límites de ese espacio, no importa si tiene un montón fijo o flotante. Para el caso, ¿cómo desperdician varias JVM algo más que el intercambio (que debe configurarse adecuadamente para el propósito de la máquina)?
kdgregory
2
Es una especie de diatriba, pero está informado por años de experiencia diaria escribiendo y operando una plataforma basada en Java. Si no realiza el intercambio (porque hará que su sistema no responda durante 20 minutos hasta que el proceso fuera de control se quede sin espacio para asignar) y tenga el exceso de memoria desactivado por razones de estabilidad (el asesino OOM no es muy bueno para elegir víctimas ), le importa el espacio de la máquina virtual y, al contrario de lo que implica la siguiente gente, el lanzamiento de una máquina virtual Java con -Xmx2048m asignará inmediatamente 2 GB de memoria virtual (al menos en mi Sun JVM en Linux) para un programa con una variable.
themel
Excelente pregunta Me he estado preguntando lo mismo. Pero, ¿cuál de los "hechos" presentados aquí en q y las respuestas son correctas?
Martin Ba
Para las reacciones que estaban buscando, simplemente deambular junto al gestor de fallos sol ... por ejemplo, aquí , aquí y aquí .
Básico

Respuestas:

23

Está usted equivocado. El tamaño de almacenamiento dinámico de las JVM no es fijo, solo está limitado:

  • -Xmx establece el tamaño máximo de memoria de almacenamiento dinámico
  • -Xms establece el tamaño mínimo de memoria de almacenamiento dinámico

Establecer un límite superior es necesario por varias razones. Primero, le dice al recolector de basura cuándo entrar en acción. En segundo lugar, evita que la JVM obstruya toda la máquina al consumir demasiada memoria. El tamaño mínimo de almacenamiento dinámico probablemente sea útil para reservar la cantidad de memoria que el programa necesita al menos, para evitar que se quede sin memoria (porque otros procesos consumen demasiado).

usuario281377
fuente
99
no el min es el valor predeterminado para evitar un inicio lento cuando una gran cantidad tiene que asignar resultando en aumentos montón repetidas, paginación se ocupará de la RAM física acabando
trinquete monstruo
@ratchetfreak esa fue mi segunda suposición ;-)
user281377
@ user281377 Si este es el caso, ¿cómo puede C # funcionar bien sin un tamaño máximo de memoria de almacenamiento dinámico?
cmorse
cmorse: solo puedo adivinar. Quizás Java esté dirigido a servidores grandes, donde muchas aplicaciones comparten recursos y son deseables los límites estrictamente impuestos, mientras que .net está hecho para PC y servidores más pequeños y dedicados.
usuario281377
@ user281377: Las aplicaciones Java que he usado que se quedan sin espacio de almacenamiento dinámico generalmente lo manejan muy mal, generalmente se bloquean o se vuelven muy inestables a partir de entonces. Y ASP.net se ejecuta en servidores grandes y pequeños muy bien. Lo que realmente no entiendo es por qué, por defecto, Java impone este límite. Me encantaría saber del razonamiento detrás de su decisión ... Estoy seguro de que tenían una buena razón.
cmorse
6

Me imagino que la respuesta tiene algo que ver con la herencia de Java. Originalmente fue diseñado como un lenguaje para ser utilizado para sistemas integrados, donde obviamente los recursos están limitados y no desea que los procesos simplemente engullen lo que esté disponible. También ayuda con la administración del sistema, ya que facilita el aprovisionamiento de recursos en un servidor si puede establecer límites de recursos. Veo que las últimas JVM parecen usar múltiples montones no continuos, aunque, por supuesto, todo aparece como un montón único en su código.

(FWIW, tenía que especificar los requisitos de memoria de un programa en las versiones de Apple anteriores a Darwin MacOS [hasta System 7 de todos modos, que fue la última que usé], que estaba bien entrado en los años 80).

TMN
fuente
1
+1 - por ser (1) la única respuesta que realmente abordó la pregunta, y (2) ser plausible.
kdgregory
2

Debe darle al GC algún mecanismo para decirle cuándo ejecutarse, o su programa ocupará todo el espacio de memoria virtual. Hay muchas formas alternativas de desencadenar GC: tiempo transcurrido, número de asignaciones, número de asignaciones, probablemente otras que no puedo pensar en este momento. En mi opinión, ninguno de esos es tan bueno como simplemente establecer un límite de memoria y ejecutar GC cuando el espacio asignado alcanza ese límite.

La clave es establecer los límites correctos . Miro -mscomo "esta es la cantidad de memoria que necesita mi aplicación" y -mxcomo "nunca debe exceder esta cantidad". En una implementación de producción, los dos deben estar cerca, si no son iguales, y deben basarse en requisitos reales medidos.

Sus preocupaciones sobre la memoria virtual "desperdiciada" están fuera de lugar: es virtual, es (casi) gratis. Sí, asignar una asignación demasiado grande al montón significa que no puede iniciar tantos subprocesos ni cargar tantos archivos asignados en memoria. Pero eso es parte del diseño de la aplicación: tiene recursos escasos, necesita particionarlos de manera que permita que su aplicación se ejecute. En un montón de "estilo C", que se expandirá hasta llegar a la parte superior de la memoria, el problema básico es el mismo, simplemente no tiene que pensarlo hasta que esté en problemas.

Lo único que un gran montón podría "desperdiciar" es el espacio de intercambio, porque todos los segmentos de escritura requieren un compromiso del intercambio. Pero eso es parte del diseño del sistema: si desea que se ejecuten muchas JVM en el mismo cuadro, aumente su intercambio o disminuya su asignación de almacenamiento dinámico. Si comienzan a sacudirse, entonces estás tratando de hacer demasiado con el sistema; compre más memoria (y si todavía está ejecutando un procesador de 32 bits, compre una nueva caja).

kdgregory
fuente
1
Pero el umbral para ejecutar GC no tiene que tener nada que ver con un umbral fijo que el uso total de memoria por parte del programa no pueda exceder. (De hecho, probablemente no debería; si tiene un montón grande y solo puede GC cuando está lleno, necesariamente tiene períodos de GC raros pero largos). Ver recolección de basura generacional.
Ben
@Ben - sí, tienes razón. Y mi segunda oración señala que hay alternativas. Sin embargo, no estoy de acuerdo con que un montón de tamaño fijo sea la forma incorrecta de administrar GC en el caso general. Una JVM ajustada correctamente utiliza el tamaño de almacenamiento dinámico "correcto"; en mi experiencia, se producen largas pausas de GC cuando la JVM no se ha sintonizado correctamente. Y, a menudo, el tamaño de almacenamiento dinámico "correcto" es mucho más pequeño de lo que parece.
kdgregory
-2

Como dice user281377, solo especifica un límite superior de la cantidad de memoria que puede consumir su proceso . Por supuesto, la aplicación en sí solo tomará el espacio que necesita.

Si debe existir un límite superior predeterminado o no, es otra cuestión, tanto a favor como en contra.

dagnelies
fuente