usage not leaks leak exist example debug java memory-management memory-leaks garbage-collection

java - leaks - memory leak monitor jar is not exist



Uso de memoria de ráfaga en Java (8)

... pero siempre me parece que una vez que Java toca algo de memoria, se va para siempre. Usted nunca va a recuperarlo.

Depende de lo que quiere decir con "ido para siempre".

También escuché decir que algunas JVM devuelven la memoria al sistema operativo cuando están listas y en condiciones de hacerlo. Desafortunadamente, dada la forma en que las API de memoria de bajo nivel suelen funcionar, la JVM tiene que devolver segmentos enteros, y suele ser complicado "evacuar" un segmento para que pueda devolverse.

Pero no confiaría en eso ... porque hay varias cosas que podrían evitar que se devuelva la memoria. Lo más probable es que la JVM no devuelva la memoria al sistema operativo. Pero no se "ha ido para siempre" en el sentido de que la JVM continuará usándola. Incluso si la JVM nunca se acerca al uso máximo nuevamente, toda esa memoria ayudará a que el recolector de basura funcione de manera más eficiente.

En ese caso, debe asegurarse de que su memoria pico nunca sea demasiado alta, o su aplicación consumirá continuamente cientos de MB de RAM.

Eso no es verdad. Suponiendo que está adoptando la estrategia de comenzar con un pequeño montón y dejarlo crecer, la JVM no solicitará una memoria significativamente mayor que la memoria pico. La JVM no consumirá continuamente más memoria ... a menos que su aplicación tenga una pérdida de memoria y (como resultado) su requisito de memoria máxima no tiene límite.

(Los comentarios del PO a continuación indican que esto no es lo que estaba tratando de decir. Aun así, es lo que dijo).

Sobre el tema de la eficiencia de recolección de basura, podemos modelar el costo de una ejecución de un recolector de basura eficiente como:

cost ~= (amount_of_live_data * W1) + (amount_of_garbage * W2)

donde W1 y W2 son (suponemos) constantes que dependen del colector. (En realidad, esto es una simplificación excesiva. La primera parte no es una función lineal del número de objetos en vivo. Sin embargo, afirmo que no tiene importancia para lo siguiente).

La eficiencia del colector se puede establecer como:

efficiency = cost / amount_of_garbage_collected

que (si suponemos que el GC recopila todos los datos) se expande a

efficiency ~= (amount_of_live_data * W1) / amount_of_garbage + W2.

Cuando el GC funciona,

heap_size ~= amount_of_live_data + amount_of_garbage

asi que

efficiency ~= W1 * (amount_of_live_data / (heap_size - amount_of_live_data) ) + W2.

En otras palabras:

  • a medida que aumenta el tamaño del montón, la eficiencia tiende a ser constante (W2), pero
  • necesita una proporción grande de heap_size a amount_of_live_data para que esto suceda.

El otro punto es que para un colector de copias eficiente, W2 cubre solo el costo de poner a cero el espacio ocupado por los objetos basura en ''desde el espacio''. El resto (rastrear, copiar objetos vivos a ''espacio'' y poner a cero ''desde el espacio'' que ocuparon) es parte del primer término de la ecuación inicial, es decir, cubierto por W1. Lo que esto significa es que W2 es probable ser considerablemente más pequeño que W1 ... y que el primer término de la ecuación final es significativo por más tiempo.

Ahora, obviamente, este es un análisis teórico, y el modelo de costos es una simplificación de cómo realmente funcionan los recolectores de basura. (Y no tiene en cuenta el trabajo "real" que está haciendo la aplicación, ni los efectos a nivel del sistema de inmovilizar demasiada memoria.) Sin embargo, las matemáticas me dicen que desde el punto de vista de la eficiencia del GC , un gran montón realmente ayuda mucho .

Estoy tratando de manejar el uso adecuado de la memoria y la recolección de basura en Java. No soy un programador novato de ninguna manera, pero siempre me parece que una vez que Java toca algo de memoria, nunca se lanzará para otras aplicaciones. En ese caso, debe asegurarse de que su memoria pico nunca sea demasiado alta, o su aplicación utilizará continuamente el uso máximo de la memoria.

Escribí un pequeño programa de muestra tratando de demostrar esto. Básicamente tiene 4 botones ...

  1. Rellena la variable de ámbito de clases BigList = new ArrayList<string>() con aproximadamente 25,000,000 de elementos de cadena largos.
  2. Llamar a BigList.clear()
  3. Reasigne la lista - BigList = new ArrayList<string>() nuevamente (para reducir el tamaño de la lista)
  4. Una llamada a System.gc() - Sí, sé que esto no significa que GC realmente se ejecutará, pero es lo que tenemos.

Luego, hice algunas pruebas en Windows, Linux y Mac OS mientras usaba los monitores de tareas predeterminados para verificar el uso de la memoria reportada por los procesos. Esto es lo que encontré ...

  • Windows : si se bombea la lista, se cancela la llamada y se llama a GC varias veces, no se reducirá el uso de memoria. Sin embargo, la reasignación de la lista usando new y llamando al GC varias veces reducirá el uso de memoria a los niveles iniciales. OMI, esto es aceptable.
  • Linux (utilicé la distribución de Mint 11 con Sun JVM) - Mismos resultados que Windows.
  • Mac OS - Seguí los pasos de sames como se indica arriba, pero incluso cuando reinicializar las llamadas de lista a GC aparentemente no tienen ningún efecto. El programa se sentará utilizando cientos de MB de RAM, aunque no tengo nada en la memoria.

¿Puede alguien explicarme esto? Algunas personas me han contado algunas cosas sobre la memoria del "montón", pero todavía no lo entiendo del todo y no estoy seguro de que se aplique aquí. Por lo que he escuchado al respecto, no debería estar viendo el comportamiento que estoy en Windows y Linux de todos modos.

¿Es esto solo una diferencia en la forma en que el Monitor de actividad de Mac OS mide el uso de la memoria o hay algo más en juego? Preferiría no tener mi programa al ralentí con toneladas de uso de RAM. Gracias por tu visión.


Hay algunos documentos geniales producidos por Sun / Oracle que describen la recolección de basura de Java. Una búsqueda rápida en "Java Garbage Collection Tuning" arroja resultados como; http://www.oracle.com/technetwork/java/gc-tuning-5-138395.html y http://java.sun.com/docs/hotspot/gc1.4.2/

La introducción de los estados doc de Oracle;

La plataforma Java TM 2 Platform Standard Edition (plataforma J2SE TM) se utiliza para una amplia variedad de aplicaciones, desde pequeños applets en escritorios hasta servicios web en servidores grandes. En la plataforma J2SE versión 1.4.2 había cuatro recolectores de basura entre los que elegir, pero sin una elección explícita por parte del usuario, siempre se elegía el recolector de basura en serie. En la versión 5.0, la elección del recopilador se basa en la clase de la máquina en la que se inicia la aplicación.

Esta "opción más inteligente" del recolector de basura generalmente es mejor, pero no siempre es la mejor. Para el usuario que desea hacer su propia elección de recolectores de basura, este documento proporcionará información sobre la cual basar esa elección. Esto incluirá primero las características generales de las colecciones de basura y las opciones de ajuste para aprovechar al máximo estas características. Los ejemplos se dan en el contexto del coleccionista serial, stop-the-world. Luego se discutirán las características específicas de los otros colectores junto con los factores que se deben considerar al elegir uno de los otros colectores.

Describen los distintos tipos de colectores disponibles y las situaciones en las que deberían usarse. Recuerdo usar esto junto con JConsole para documentar cómo se realizó la aplicación cuando se inició con varias opciones diferentes.

Estos documentos le darán un poco más de información sobre cómo se produce la recopilación dependiendo de los parámetros que esté utilizando.


Java asigna memoria solo a los objetos. No hay una asignación explícita de memoria. De hecho, Java incluso trata los tipos de matriz como objetos. Cada vez que un objeto lo crea, viene en montón.

El tiempo de ejecución de Java emplea un recolector de basura que recupera la memoria ocupada por un objeto una vez que determina que ese objeto ya no es accesible. Este es un proceso automático.

Llamar a System.gc() no puede recolectar basura al momento de llamarlo; Por eso tu memoria no se reduce. En general, es mejor dejar que el sistema decida cuándo necesita recolectar el montón, y si debe o no hacer una colección completa.

System.gc() ni siquiera fuerza una recolección de basura; es simplemente una pista para la JVM que "ahora puede ser un buen momento para limpiar un poco"

La memoria de Java se explica here link2


Me encontré con este problema en Windows y encontré una solución, así que lo publico como una respuesta en caso de que pueda ayudar a otros.

Muchas respuestas aquí sugieren que el comportamiento de Java es 1. bueno y / o 2. una consecuencia inevitable de la recolección de basura. Estos son ambos falsos.

El problema:

Si eres como yo y quieres escribir Java para escribir aplicaciones pequeñas para una estación de trabajo o incluso ejecutar múltiples procesos más pequeños en un servidor, el comportamiento de asignación de memoria JVM de Oracle lo hace casi completamente inútil. Incluso cuando se ejecuta con -client, cada proceso de JVM acumula memoria una vez asignada y nunca la devuelve. Este comportamiento no puede ser deshabilitado. Como lo notan OP: cada proceso de jvm conserva su memoria no utilizada de forma indefinida, incluso si nunca volverá a usarla e incluso mientras otros procesos de jvm se mueren de hambre. Este comportamiento inexplicable hace que Oracle sea una implementación inútil para todos los escenarios monolíticos de una sola aplicación.

Además: esto NO es una consecuencia de la recolección de basura. Sea testigo de las aplicaciones .Net que se ejecutan en Windows, utilice la recolección de elementos no utilizados y no sufra este problema en absoluto.

La solución:

La solución que encontré para esto fue utilizar IKVM.NET JVM, que se usa como reemplazo directo para java.exe en Windows. Compila el bytecode de Java al código .Net IL y se ejecuta como un proceso .Net. También contiene utilidades para convertir archivos .jar en ensamblados .Net .dll y .exe. El rendimiento a menudo es mejor que la JVM de Oracle y después de un GC, la memoria se devuelve instantáneamente al sistema operativo. (Nota: esto también funciona en Linux con Mono)

Para ser claro, todavía confío en la JVM de Oracle para todas mis aplicaciones pequeñas y también para depurar mis pequeñas aplicaciones, pero una vez estable, uso ikvm para ejecutarlas como si fueran aplicaciones de Windows nativas y esto funciona tan bien, sorprendido Tiene numerosos efectos secundarios beneficiosos. Una vez compilados, los DLL compartidos entre procesos se cargan solo una vez y las aplicaciones aparecen en el administrador de tareas como .exe en lugar de mostrarse como javaw.exe.

Desafortunadamente, no todos pueden usar ikvm para resolver este problema, pero espero que esto ayude a aquellos en mi situación.


Sun / Oracle JVM no devuelve la memoria innecesaria al sistema. Si le da un tamaño grande y máximo de almacenamiento dinámico y realmente usa ese espacio de almacenamiento dinámico en algún momento, la JVM no lo devolverá al sistema operativo para otros usos. Otras JVM harán eso (JRockit solía hacerlo, pero no creo que lo haga más).

Por lo tanto, para Oracles JVM necesita ajustar su aplicación y su sistema para un uso máximo, así es como funciona. Si la memoria que está utilizando puede administrarse con matrices de bytes (como trabajar con imágenes o algo así), puede utilizar búferes de bytes mapeados en lugar de matrices de bytes Java. Los búferes de bytes mapeados se toman directamente del sistema y no son parte del montón. Cuando liberas estos objetos (Y están GC, creo, pero no estoy seguro), la memoria regresará al sistema. Es probable que tengas que jugar con eso suponiendo que sea aplicable en absoluto.


Una idea errónea común es que Java usa la memoria mientras se ejecuta y allí debería poder devolver la memoria al sistema operativo. En realidad, Oracle / Sun JVM reserva la memoria virtual como un bloque continuo de memoria tan pronto como se inicia. Si no hay suficiente memoria virtual continua disponible, falla al iniciarse, incluso si el programa no va a usar tanto.

Entonces, lo que sucede es que el sistema operativo es lo suficientemente inteligente como para no asignar memoria física al programa hasta que no se use. No puede recuperar fácilmente la memoria, pero puede cambiarse al disco si es necesario y no se ha utilizado durante un tiempo. Java no maneja tener partes del montón intercambiadas en el disco muy bien, así que esto debe evitarse.


Una vez que el programa finaliza, ¿el uso de la memoria se reduce en el administrador de tareas en Windows? Creo que la memoria se está liberando pero no se muestra como liberada por los monitores de tareas predeterminados en el sistema operativo que está monitoreando. Repase esta pregunta sobre C ++ Problema con la desasignación del vector de punteros


La mayoría de las JVM no pueden o no pueden devolver la memoria previamente adquirida al sistema operativo host si no se necesita atm. Esto se debe a que es una tarea costosa y compleja. El recolector de basura solo se aplica a la memoria del montón dentro de la máquina virtual Java. Por lo tanto, no devuelve (memoria free() en C ) al sistema operativo. Por ejemplo, si un objeto grande ya no se usa, el GC marcará la memoria como libre dentro del montón de la JVM y no se liberará al sistema operativo.