2011-04-19 14 views
14

Estoy usando una clase interna que es una subclase de HashMap. Tengo un String como clave y double[] como valores. Guardo alrededor de 200 dobles por double[]. Debería usar alrededor de 700   MB para almacenar las claves, los punteros y los dobles. Sin embargo, el análisis de memoria revela que necesito mucho más que eso (un poco más de 2   GB).Serializable, clonable y uso de memoria en Java

Usando TIJmp (herramienta de perfilado) vi que había un char[] que estaba usando casi la mitad de la memoria total. TIJmp dijo que char[] provino de Serializable y Cloneable. Los valores que contiene van desde una lista de fuentes y rutas predeterminadas a mensajes y caracteres individuales.

¿Cuál es el comportamiento exacto de Serializable en la JVM? ¿Mantiene una copia "persistente" todo el tiempo, doblando el tamaño de la huella de mi memoria? ¿Cómo puedo escribir copias binarias de un objeto en tiempo de ejecución sin convertir la JVM en una memoria parabólica?

PD: El método donde el consumo de memoria aumenta más es el siguiente. El archivo tiene alrededor de 229,000 líneas y 202 campos por línea.

public void readThetas(String filename) throws Exception 
{ 
    long t1 = System.currentTimeMillis(); 
    documents = new HashMapX<String,double[]>(); //Document names to indices. 
    Scanner s = new Scanner(new File(filename)); 
    int docIndex = 0; 
    if (s.hasNextLine()) 
     System.out.println(s.nextLine()); // Consume useless first line :) 
    while(s.hasNextLine()) 
    { 
     String[] fields = s.nextLine().split("\\s+"); 
     String docName = fields[1]; 
     numTopics = fields.length/2-1; 
     double[] thetas = new double[numTopics]; 
     for (int i=2;i<numTopics;i=i+2) 
      thetas[Integer.valueOf(fields[i].trim())] = Double.valueOf(fields[i+1].trim()); 
     documents.put(docName,thetas); 
     docIndex++; 
     if (docIndex%10000==0) 
      System.out.print("*"); //progress bar ;) 
    } 
    s.close(); 
    long t2 = System.currentTimeMillis(); 
    System.out.println("\nRead file in "+ (t2-t1) +" ms"); 
} 

Ah !, y HashMapX es una clase interna declarada como esto:

public static class HashMapX< K, V> extends HashMap<K,V> { 
    public V get(Object key, V altVal) { 
     if (this.containsKey(key)) 
      return this.get(key); 
     else 
      return altVal; 
    } 
} 
+0

¿Puede mostrar algunas muestras de código? – axtavt

+1

Por favor, publique las pruebas que muestran que Serializable aumenta la huella de memoria. Si pudieras publicar el código que muestra mucha RAM, tu Map está usando eso también. –

+0

Déjame ver si entiendo tu declaración allí. ¿Estás diciendo que al declarar una clase Serializable el tamaño ocupado por instancias de este es mayor que si fuera transitorio? –

Respuesta

4

Entonces, encontré la respuesta. Es una pérdida de memoria en mi código. No tenía nada que ver con Serializable o Cloneable.

Este código intenta analizar un archivo. Cada línea contiene un conjunto de valores que estoy tratando de extraer. Luego, conservo algunos de esos valores y los almaceno en HashMapX u otra estructura.

El núcleo del problema es aquí:

 String[] fields = s.nextLine().split("\\s+"); 
     String docName = fields[1]; 

y propagarla aquí:

 documents.put(docName,thetas); 

Lo que pasa es que docName es una referencia a un elemento de una matriz (campos) y Mantengo esa referencia durante la vida del programa (al almacenarla en los documentos globales de HashMap). Mientras mantenga viva esa referencia, no se pueden recolectar los campos String [] completos. La solución:

 String docName = new String(fields[1]); // A copy, not a reference. 

Copiando así el objeto y soltando la referencia al elemento de la matriz. De esta forma, el recolector de basura puede liberar la memoria utilizada por la matriz una vez que procese cada campo.

Espero que esto sea útil para todos aquellos que analizan archivos de texto grandes usando dividir y almacenar algunos de los campos en variables globales.

Gracias a todos por sus comentarios. Ellos me guiaron en la dirección correcta.

5

Esto no puede hacer frente a todas sus preguntas, pero es una manera en la que la serialización puede aumentar significativamente el uso de memoria: http://java.sun.com/javase/technologies/core/basic/serializationFAQ.jsp#OutOfMemoryError .

En resumen, si mantiene abierto un ObjectOutputStream, ninguno de los objetos que se han escrito en él puede recogerse a menos que llame explícitamente al método reset().

+1

Esta es una buena pista, siempre que los objetos en cuestión se serialicen realmente, porque la publicación original solo sugiere un aumento de memoria al hacer las clases serializables y el desarrollador realizó pruebas en clases ficticias no serializables y la huella de memoria determinada fue menor (sin embargo, no sé cómo se llevó a cabo esta evaluación), pero si este es el caso, entonces el caso raíz debería ser otra cosa. Honestamente, estoy demasiado inclinado a creer que tu explicación es la más lógica hasta ahora. –

Cuestiones relacionadas