Estoy leyendo un archivo muy grande y extrayendo algunas pequeñas porciones de texto de cada línea. Sin embargo, al final de la operación, me queda muy poca memoria para trabajar. Parece que el recolector de basura no puede liberar memoria después de leer en el archivo.Java no recolección de basura memoria
Mi pregunta es: ¿hay alguna manera de liberar esta memoria? ¿O es esto un error de JVM?
Creé un SSCCE para demostrar esto. Lee en un archivo de 1 mb (2 mb en Java debido a la codificación de 16 bits) y extrae un carácter de cada línea (~ 4000 líneas, por lo que debería ser de aproximadamente 8 kb). ¡Al final de la prueba, todavía se usan los 2 mb completos!
El uso de la memoria inicial:
Allocated: 93847.55 kb
Free: 93357.23 kb
Inmediatamente después de leer en el archivo (antes de cualquier recogida de basura manual):
Allocated: 93847.55 kb
Free: 77613.45 kb (~16mb used)
Esto es de esperarse ya que el programa está utilizando una gran cantidad de recursos para leer en el archivo.
Sin embargo luego recoger la basura, pero no todos se libera la memoria:
Allocated: 93847.55 kb
Free: 91214.78 kb (~2 mb used! That's the entire file!)
Sé que llamar manualmente el recolector de basura no le da ninguna garantía (en algunos casos es perezoso). Sin embargo, esto estaba sucediendo en mi aplicación más grande, donde el archivo consume casi toda la memoria disponible y hace que el resto del programa se quede sin memoria a pesar de la necesidad. Este ejemplo confirma mi sospecha de que el exceso de datos leídos del archivo no se libera.
Aquí es el SSCCE para generar la prueba:
import java.io.*;
import java.util.*;
public class Test {
public static void main(String[] args) throws Throwable {
Runtime rt = Runtime.getRuntime();
double alloc = rt.totalMemory()/1000.0;
double free = rt.freeMemory()/1000.0;
System.out.printf("Allocated: %.2f kb\nFree: %.2f kb\n\n",alloc,free);
Scanner in = new Scanner(new File("my_file.txt"));
ArrayList<String> al = new ArrayList<String>();
while(in.hasNextLine()) {
String s = in.nextLine();
al.add(s.substring(0,1)); // extracts first 1 character
}
alloc = rt.totalMemory()/1000.0;
free = rt.freeMemory()/1000.0;
System.out.printf("Allocated: %.2f kb\nFree: %.2f kb\n\n",alloc,free);
in.close();
System.gc();
alloc = rt.totalMemory()/1000.0;
free = rt.freeMemory()/1000.0;
System.out.printf("Allocated: %.2f kb\nFree: %.2f kb\n\n",alloc,free);
}
}
A menos que esté haciendo algo muy inusual que probablemente nadie más esté haciendo, "jvm error" no debería ser su primera suposición. –
Especialmente con respecto al gc. –
¿Cómo esperas que System.gc() libere toda la memoria? Todavía estás usando las cuerdas en al, para que no puedan ser liberadas. –