¿Cómo puedo crear una pérdida de memoria en Java?

Resuelto MatBanik asked hace 13 años • 61 respuestas

Acabo de tener una entrevista en la que me pidieron que creara una pérdida de memoria con Java.

No hace falta decir que me sentí bastante tonto al no tener idea de cómo empezar a crear uno.

¿Cuál sería un ejemplo?

MatBanik avatar Jun 24 '11 23:06 MatBanik
Aceptado

Aquí hay una buena manera de crear una verdadera pérdida de memoria (objetos inaccesibles al ejecutar código pero aún almacenados en la memoria) en Java puro:

  1. La aplicación crea un subproceso de larga duración (o utiliza un grupo de subprocesos para filtrar aún más rápido).
  2. El hilo carga una clase a través de un archivo (opcionalmente personalizado) ClassLoader.
  3. La clase asigna una gran cantidad de memoria (por ejemplo new byte[1000000]), almacena una fuerte referencia a ella en un campo estático y luego almacena una referencia a sí misma en un archivo ThreadLocal. Asignar memoria adicional es opcional (filtrar la instancia de clase es suficiente), pero hará que la fuga funcione mucho más rápido.
  4. La aplicación borra todas las referencias a la clase personalizada o ClassLoaderdesde donde se cargó.
  5. Repetir.

Debido a la forma en ThreadLocalque se implementa en el JDK de Oracle, esto crea una pérdida de memoria:

  • Cada uno Threadtiene un campo privado threadLocals, que en realidad almacena los valores locales del hilo.
  • Cada clave en este mapa es una referencia débil a un ThreadLocalobjeto, por lo que después de que ese ThreadLocalobjeto se recolecta como basura, su entrada se elimina del mapa.
  • Pero cada valor es una referencia fuerte, por lo que cuando un valor (directa o indirectamente) apunta al ThreadLocalobjeto que es su clave , ese objeto no será recolectado como basura ni eliminado del mapa mientras el hilo esté vivo.

En este ejemplo, la cadena de referencias fuertes se ve así:

Threadobjeto → threadLocalsmapa → instancia de clase de ejemplo → clase de ejemplo → ThreadLocalcampo estático → ThreadLocalobjeto.

(En ClassLoaderrealidad, no juega un papel en la creación de la fuga, simplemente la empeora debido a esta cadena de referencia adicional: clase de ejemplo → ClassLoader→ todas las clases que ha cargado. Fue aún peor en muchas implementaciones de JVM, especialmente antes de Java 7, porque las clases y ClassLoaders se asignaron directamente a permgen y nunca se recolectaron basura).

Una variación de este patrón es la razón por la que los contenedores de aplicaciones (como Tomcat) pueden perder memoria como un colador si con frecuencia vuelves a implementar aplicaciones que usan ThreadLocalcorreos electrónicos que de alguna manera apuntan a sí mismos. Esto puede suceder por varias razones sutiles y, a menudo, es difícil de depurar y/o solucionar.

Actualización : dado que mucha gente sigue solicitándolo, aquí hay un código de ejemplo que muestra este comportamiento en acción .

Daniel Pryden avatar Jun 24 '2011 18:06 Daniel Pryden

Campo estático que contiene una referencia de objeto [especialmente un campo final ]

class MemorableClass {
    static final ArrayList list = new ArrayList(100);
}

Secuencias abiertas (no cerradas) (archivo, red, etc.)

try {
    BufferedReader br = new BufferedReader(new FileReader(inputFile));
    ...
    ...
} catch (Exception e) {
    e.printStackTrace();
}

Conexiones no cerradas

try {
    Connection conn = ConnectionFactory.getConnection();
    ...
    ...
} catch (Exception e) {
    e.printStackTrace();
}

Áreas a las que no se puede acceder desde el recolector de basura de JVM , como la memoria asignada mediante métodos nativos.

En las aplicaciones web, algunos objetos se almacenan en el ámbito de la aplicación hasta que la aplicación se detiene o elimina explícitamente.

getServletContext().setAttribute("SOME_MAP", map);

Opciones de JVM incorrectas o inapropiadas , como la noclassgcopción en IBM JDK que impide la recolección de basura de clases no utilizadas

Consulte Configuración de IBM JDK .

Prashant Bhate avatar Jul 01 '2011 13:07 Prashant Bhate