java - instalar - install jdk ubuntu
¿OpenJDK JVM alguna vez devolverá memoria de montón a Linux? (1)
G1 (-XX: + UseG1GC), Búsqueda paralela (-XX: + UseParallelGC) y ParallelOld (-XX: + UseParallelOldGC) devuelven memoria cuando el montón se encoge. No estoy tan seguro sobre Serial y CMS, no redujeron su montón en mis experimentos.
Ambos colectores paralelos requieren una cantidad de GC antes de reducir el montón a un tamaño "aceptable". Esto es por diseño. Están aferrándose deliberadamente al montón suponiendo que será necesario en el futuro. Establecer el indicador -XX: GCTimeRatio = 1 mejorará un poco la situación, pero aún se necesitarán varios GC para reducirse mucho.
G1 es notablemente bueno para reducir el montón rápidamente, por lo que para el caso de uso descrito anteriormente, diría que es solucionable usando G1 y ejecutando
System.gc()
después de haber liberado todos los cachés y cargadores de clases, etc.
Tenemos un proceso de servidor de larga duración, que con poca frecuencia necesita mucha RAM por un corto tiempo. Vemos que una vez que la JVM ha obtenido la memoria del sistema operativo, nunca la devuelve al sistema operativo. ¿Cómo le pedimos a la JVM que devuelva la memoria de almacenamiento dinámico al sistema operativo?
Típicamente, la respuesta aceptada a tales preguntas es usar
-XX:MaxHeapFreeRatio
y
-XX:MinHeapFreeRatio
.
(Ver, por ejemplo,
1
,
2
,
3
,
4
).
Pero estamos ejecutando Java así:
java -Xmx4G -XX:MaxHeapFreeRatio=50 -XX:MinHeapFreeRatio=30 MemoryUsage
y todavía veo esto en VisualVM:
Claramente, la JVM no está cumpliendo
-XX:MaxHeapFreeRatio=50
ya que heapFreeRatio está muy cerca del 100% y no está cerca del 50%.
Ninguna cantidad de clics en "Realizar GC" devuelve la memoria al sistema operativo.
MemoryUsage.java:
import java.util.ArrayList;
import java.util.List;
public class MemoryUsage {
public static void main(String[] args) throws InterruptedException {
System.out.println("Sleeping before allocating memory");
Thread.sleep(10*1000);
System.out.println("Allocating/growing memory");
List<Long> list = new ArrayList<>();
// Experimentally determined factor. This gives approximately 1750 MB
// memory in our installation.
long realGrowN = 166608000; //
for (int i = 0 ; i < realGrowN ; i++) {
list.add(23L);
}
System.out.println("Memory allocated/grown - sleeping before Garbage collecting");
Thread.sleep(10*1000);
list = null;
System.gc();
System.out.println("Garbage collected - sleeping forever");
while (true) {
Thread.sleep(1*1000);
}
}
}
Versiones
> java -version
openjdk version "1.8.0_66-internal"
OpenJDK Runtime Environment (build 1.8.0_66-internal-b01)
OpenJDK 64-Bit Server VM (build 25.66-b01, mixed mode)
> uname -a
Linux londo 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt11-1+deb8u5 (2015-10-09) x86_64 GNU/Linux
> lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 8.2 (jessie)
Release: 8.2
Codename: jessie
También probé OpenJDK 1.7 y Sun Java''s 1.8. Todos se comportan de manera similar y ninguno devuelve la memoria al sistema operativo.
Creo que necesito esto y que el intercambio y la paginación no "resolverán" esto, porque gastar el disco IO en paginar cerca de 2GB de basura es solo un desperdicio de recursos. Si no está de acuerdo, por favor, ilumíneme.
También he escrito un poco de memoryUsage.c con
malloc()
/
free()
, y devuelve la memoria al sistema operativo.
Entonces es
posible
en C. ¿Quizás no con Java?
Editar: Augusto señaló que la búsqueda me habría llevado a
-XX:MaxHeapFreeRatio
y
-XX:MinHeapFreeRatio
solo funcionaba con
-XX:+UseSerialGC
.
Estaba extasiado y lo intenté, desconcertado de no haberlo encontrado.
Sí, funcionó con mi MemoryUsage.java:
Sin embargo, cuando probé
-XX:+UseSerialGC
con nuestra aplicación real, no tanto:
Descubrí que gc () después de un tiempo ayudó, así que hice un hilo que hizo más o menos:
while (idle() && memoryTooLarge() && ! tooManyAttemptsYet()) {
Thread.sleep(10*1000);
System.gc();
}
y eso hizo el truco:
En realidad, anteriormente había visto el comportamiento con
-XX:+UseSerialGC
y múltiples llamadas
System.gc()
en algunos de mis muchos experimentos, pero no me gustó la necesidad de un hilo GC.
Y quién sabe si eso seguirá funcionando mientras nuestra aplicación y Java evolucionen.
Debe haber una mejor manera.
¿Cuál es la lógica que me obliga a llamar a
System.gc()
cuatro veces (pero no de inmediato), y dónde está documentado todo esto?
En la búsqueda de documentación para
-XX:MaxHeapFreeRatio
y
-XX:MinHeapFreeRatio
solo funciona con
-XX:+UseSerialGC
, leí
la documentación para la herramienta / ejecutable de Java
y no se menciona en ningún lado que
-XX:MaxHeapFreeRatio
y
-XX:MinHeapFreeRatio
solo funciona con
-XX:+UseSerialGC
.
De hecho, el problema solucionado
[JDK-8028391] Hacer manejables las marcas Min / MaxHeapFreeRatio
dice:
Para permitir que las aplicaciones controlen cómo y cuándo permitir más o menos GC, los indicadores -XX: MinHeapFreeRatio y -XX: MaxHeapFreeRatio deben hacerse manejables. El soporte para estos indicadores también debe implementarse en el recopilador paralelo predeterminado.
Un comment para el problema solucionado dice:
El soporte para estos indicadores también se ha agregado a ParallelGC como parte de la política de tamaño adaptativo.
Lo he comprobado, y el
patch
referencia en el
problema solucionado que se hizo backport a openjdk-8
está contenido en el
paquete fuente tarball
para la versión de openjdk-8 que estoy usando.
Por lo tanto, aparentemente debería funcionar en "el colector paralelo predeterminado", pero no como lo he demostrado en esta publicación.
Todavía no he encontrado ninguna documentación que diga que solo debería funcionar con
-XX:+UseSerialGC
.
Y como he documentado aquí, incluso esto no es confiable / incierto.
¿No puedo hacer que
-XX:MaxHeapFreeRatio
y
-XX:MinHeapFreeRatio
hagan lo que prometen sin tener que pasar por todos estos obstáculos?