2009-03-23 21 views
18

Tengo una pregunta sobre la gestión de memoria JVM (al menos para la de SUN).JVM devolviendo memoria a OS

Me gustaría saber cómo controlar el hecho de que la JVM devuelve la memoria no utilizada al sistema operativo (Windows en mi caso).

Escribí un programa simple de Java para ilustrar lo que esperaba. Ejecútelo con la opción -Dcom.sun.management.jmxremote para que también pueda supervisar el montón con jconsole, por ejemplo.

con el siguiente programa:

package fr.brouillard.jvm; 

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.util.LinkedList; 
import java.util.List; 

public class MemoryFree { 
    private BufferedReader reader = new BufferedReader(new 
     InputStreamReader(System.in)); 
    private List<byte[]> usedMemory = new LinkedList<byte[]>(); 
    private int totalMB = 0; 
    private int gcTimes = 0; 

    public void allocate(int howManyMB) { 
     usedMemory.add(new byte[howManyMB * 1024 * 1024]); 
     totalMB += howManyMB; 
     System.out.println(howManyMB + "MB allocated, total allocated: " + 
       totalMB + "MB"); 
    } 

    public void free() { 
     usedMemory.clear(); 
    } 

    public void gc() { 
     System.gc(); 
     System.out.println("GC " + (++gcTimes) + " times"); 
    } 

    public void waitAnswer(String msg) { 
     System.out.println("Press [enter]" + ((msg==null)?"":msg)); 
     try { 
      reader.readLine(); 
     } catch (IOException e) { 
     } 
    } 

    public static void main(String[] args) { 
     MemoryFree mf = new MemoryFree(); 
     mf.waitAnswer(" to allocate memory"); 
     mf.allocate(20); 
     mf.allocate(10); 
     mf.allocate(15); 
     mf.waitAnswer(" to free memory"); 
     mf.free(); 
     mf.waitAnswer(" to GC"); 
     mf.gc(); 
     mf.waitAnswer(" to GC"); 
     mf.gc(); 
     mf.waitAnswer(" to GC"); 
     mf.gc(); 
     mf.waitAnswer(" to GC"); 
     mf.gc(); 
     mf.waitAnswer(" to exit the program"); 

     try { 
      mf.reader.close(); 
     } catch (IOException e) {} 
    } 
} 

El montón interna es libre una vez finalizada la primera GC (lo que se espera), pero la memoria sólo se envía de nuevo al sistema operativo a partir del tercer GC. Después de la cuarta, la memoria asignada completa se envía de vuelta al sistema operativo.

Cómo configurar la JVM para controlar este comportamiento? De hecho, mi problema es que necesito ejecutar varias sesiones de clientes CITRIX en un servidor, pero me gustaría que las JVM en ejecución en el servidor liberen la memoria lo antes posible (tengo solo unas pocas funciones de memoria de alto consumo en mi aplicación) .

Si no se puede controlar este comportamiento, puedo dejarlo así y aumentar la memoria virtual del sistema operativo y dejar que el sistema operativo lo use como lo desee sin grandes problemas de rendimiento. Por ejemplo, ¿habría problemas para tener 10 procesos Java de 1 GB de memoria (con solo 100 MB de objetos asignados reales en el montón) en un servidor de 4 GB con memoria virtual suficiente, por supuesto.

Supongo que otras personas ya se enfrentaron a tales preguntas/problemas.

Gracias por su ayuda.

+0

wow las etiquetas comieron por completo tu código –

+0

He reformateado tu fragmento de código un poco para usar las funciones de resaltado de sintaxis de Stackoverflow. Espero que no te importe –

+0

Gracias, no logré que se muestre correctamente. No estoy frustrado, sólo estoy aquí ;-) nueva –

Respuesta

15

Para controlar el retorno del montón al sistema operativo, desde Java 5 en adelante, use la opción -XX:MaxHeapFreeRatio, como se describe en tuning guide.

Si cree que su pregunta es significativamente diferente de this one, por favor señalar cómo.

+0

Gracias por su respuesta, voy a hacer algunas pruebas con esas sugerencias. –

+0

Parece que parte de su pregunta es "¿es seguro sobreasignar la memoria de la máquina si tiene memoria virtual a la que recurrir", que es diferente de la pregunta vinculada? – Kai

7

Antes que nada, System.gc() bien podría no hacer nada. Realmente no puedes confiar en que haga una recolección de basura de la manera que estás sugiriendo.

En segundo lugar, tendrá que controlar lo que está sucediendo realmente en el con GC utilizando

-verbosegc -XX:+PrintGCDetails 

en su invocación de Java. O usando JConsole, que parece que estás haciendo. Pero ese System.gc() me tiene asustado de que estés contando lo incorrecto ...

Sospecho que cuando dices que la segunda o tercera recolección de basura es cuando libera la memoria, estás contando mal las recolecciones de basura. ¡Una solicitud a GC no es un GC! Por lo tanto, compruebe los registros (interpret them this way) que PrintGCDetails imprime.

De hecho, mi problema es que se necesita para ejecutar varias sesiones de clientes de Citrix en el servidor, pero me gustaría que la JVM se ejecuta en el servidor para liberar la memoria tan pronto como sea posible (sólo tengo poca memoria de alto consumo funciones en mi aplicación).

Si bien su problema es válido, la solución que está buscando es un poco sospechosa. La JVM necesita un tamaño de pila exactamente por esta razón, para que se pueda garantizar que este espacio se ejecute. Parece que te estás inclinando hacia el lanzamiento de una aplicación, luego esperas a que la JVM dimensione su almacenamiento y luego ejecute otra. que está sobrevesando los recursos en la máquina. No hagas eso, porque todo explotará una vez que una aplicación esté tomando más memoria de la que creías que tendría, pero a la que tiene derecho.

Creo plenamente que no quiere ser micro gestión del montón de Java de esta manera.

leído lo suficiente de http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html para comprender las generaciones y cuáles son las compensaciones de un montón más grande/más pequeño son.

+0

Esto no aborda la pregunta, que trata sobre el intercambio de memoria entre el SO y la JVM, no dentro de la propia JVM. – erickson

+0

@erickson: tienes razón, elimina los comentarios internos de la pila JVM. Me pareció que estaba especificando un montón demasiado grande y no era necesario para poder asignarlo más pequeño. Sin embargo, no parece ser el caso. – Kai

0

No me preocuparía hasta que vea desaceleraciones mensurables. Si un proceso tiene memoria asignada que no está utilizando, el sistema operativo intercambiará los fragmentos no utilizados en el disco según sea necesario.

+2

Así que su sugerencia sería asignar al menos tanta memoria virtual como la cantidad de proceso que tengo multiplicado por el tamaño permitido del montón JVM de cada proceso. Por ejemplo, 10 procesos con un montón máximo de 1GO, entonces necesito más/menos 10GB de memoria virtual? –

+0

Sí, vamos a cambiar ... qué gran idea para matar por completo las actuaciones. – LtWorf