¿Cómo puedo crear una pérdida de memoria en Java?
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?
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:
- La aplicación crea un subproceso de larga duración (o utiliza un grupo de subprocesos para filtrar aún más rápido).
- El hilo carga una clase a través de un archivo (opcionalmente personalizado)
ClassLoader
. - 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 archivoThreadLocal
. Asignar memoria adicional es opcional (filtrar la instancia de clase es suficiente), pero hará que la fuga funcione mucho más rápido. - La aplicación borra todas las referencias a la clase personalizada o
ClassLoader
desde donde se cargó. - Repetir.
Debido a la forma en ThreadLocal
que se implementa en el JDK de Oracle, esto crea una pérdida de memoria:
- Cada uno
Thread
tiene un campo privadothreadLocals
, que en realidad almacena los valores locales del hilo. - Cada clave en este mapa es una referencia débil a un
ThreadLocal
objeto, por lo que después de que eseThreadLocal
objeto 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
ThreadLocal
objeto 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í:
Thread
objeto → threadLocals
mapa → instancia de clase de ejemplo → clase de ejemplo → ThreadLocal
campo estático → ThreadLocal
objeto.
(En ClassLoader
realidad, 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 ClassLoader
s 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 ThreadLocal
correos 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 .
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 noclassgc
opción en IBM JDK que impide la recolección de basura de clases no utilizadas
Consulte Configuración de IBM JDK .