recorrer metodos example ejemplos colecciones coleccion java hashmap out-of-memory

java - metodos - ¿Por qué obtengo un OutOfMemoryError al insertar 50,000 objetos en HashMap?



metodos de hashmap java (10)

Algunas personas sugieren cambiar los parámetros de HashMap para ajustar los requisitos de memoria. Sugeriría medir en lugar de adivinar ; podría ser algo más que causa el OOME. En particular, sugiero usar NetBeans Profiler o VisualVM (que viene con Java 6, pero veo que estás atascado con Java 5).

Estoy tratando de insertar aproximadamente 50,000 objetos (y por lo tanto 50,000 claves) en java.util.HashMap<java.awt.Point, Segment> . Sin embargo, sigo obteniendo una excepción de OutOfMemory. (El Segment es mi propia clase, muy liviana, un campo String y 3 campos int ).

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.HashMap.resize(HashMap.java:508) at java.util.HashMap.addEntry(HashMap.java:799) at java.util.HashMap.put(HashMap.java:431) at bus.tools.UpdateMap.putSegment(UpdateMap.java:168)

Esto parece bastante ridículo ya que veo que hay una gran cantidad de memoria disponible en la máquina, tanto en la memoria RAM libre como en el espacio de alta definición para la memoria virtual.

¿Es posible que Java se esté ejecutando con algunos requisitos de memoria estrictos? ¿Puedo aumentar estos?

¿Hay alguna limitación extraña con HashMap ? ¿Tendré que implementar el mío? ¿Hay alguna otra clase que valga la pena mirar?

(Estoy ejecutando Java 5 con OS X 10.5 en una máquina Intel con 2 GB de RAM).


De forma predeterminada, la JVM usa un espacio de montón limitado. El límite depende de la implementación de JVM, y no está claro qué JVM está utilizando. En sistemas operativos que no sean Windows, una JVM Sun de 32 bits en una máquina con 2 Gb o más utilizará un tamaño de almacenamiento máximo predeterminado de 1/4 de la memoria física, o 512 Mb en su caso. Sin embargo, el valor predeterminado para una JVM en modo "cliente" es solo un tamaño máximo de almacenamiento dinámico de 64 Mb, que puede ser lo que se haya encontrado. Las JVM de otros proveedores pueden seleccionar diferentes valores predeterminados.

Por supuesto, puede especificar el límite de montón explícitamente con la -Xmx<NN>m en java , donde <NN> es el número de megabytes para el montón.

Como una suposición aproximada, su tabla hash solo debería usar unos 16 Mb, por lo que debe haber algunos otros objetos grandes en el montón. Si pudieras usar una clave Comparable en un TreeMap , eso ahorraría algo de memoria.

Consulte "Ergonomía en la versión 5.0 JVM" para más detalles.


El espacio de almacenamiento dinámico de Java está limitado de manera predeterminada, pero eso aún suena extremo (¿pero qué tan grandes son sus 50000 segmentos?)

Estoy sospechando que tienes otro problema, como que las matrices en el conjunto crecen demasiado porque todo se asigna a la misma "ranura" (también afecta el rendimiento, por supuesto). Sin embargo, eso parece poco probable si sus puntos se distribuyen uniformemente.

Me pregunto por qué estás usando un HashMap en lugar de un TreeMap. Aunque los puntos son bidimensionales, puede subclasificarlos con una función de comparación y luego realizar búsquedas de log (n).


Implícito en estas respuestas es que Java tiene un tamaño fijo para la memoria y no crece más allá del tamaño de montón máximo configurado. Esto es diferente, por ejemplo, C, donde está restringido solo por la máquina en la que se está ejecutando.


Las implementaciones están respaldadas por matrices por lo general. Las matrices son bloques de memoria de tamaño fijo. La implementación de hashmap comienza almacenando datos en una de estas matrices a una capacidad determinada, digamos 100 objetos.

Si llena la matriz y sigue agregando objetos, el mapa necesita aumentar secretamente el tamaño de la matriz. Como las matrices son fijas, lo hace creando una matriz completamente nueva, en la memoria, junto con la matriz actual, que es un poco más grande. Esto se conoce como crecimiento de la matriz. Luego, todos los elementos de la matriz anterior se copian en la nueva matriz y la matriz anterior se desreferencia con la esperanza de que sea basura recolectada y la memoria liberada en algún momento.

Por lo general, el código que aumenta la capacidad del mapa al copiar elementos en una matriz más grande es la causa de tal problema. Hay implementaciones "tontas" e inteligentes que usan un factor de crecimiento o carga que determina el tamaño de la nueva matriz en función del tamaño de la matriz anterior. Algunas implementaciones ocultan estos parámetros y otras no, por lo que no siempre puede configurarlos. El problema es que cuando no puede configurarlo, elige algún factor de carga predeterminado, como 2. Entonces, la nueva matriz es dos veces más grande que la anterior. Ahora su supuesto mapa de 50k tiene una matriz de respaldo de 100k.

Observe si puede reducir el factor de carga a 0.25 o algo así. esto causa más colisiones de hash en el mapa, lo que perjudica el rendimiento, pero está alcanzando un cuello de botella de memoria y necesita hacerlo.

Usa este constructor:

( http://java.sun.com/javase/6/docs/api/java/util/HashMap.html#HashMap(int , float))


Otra cosa que debes probar si conoces el número de objetos de antemano es utilizar el constructor HashMap (int capacity, double loadfactor) en lugar del constructor no-arg predeterminado que usa los valores predeterminados de (16,0.75). Si el número de elementos en su HashMap excede (capacidad * factor de carga), entonces la matriz subyacente en el HashMap se redimensionará a la siguiente potencia de 2 y se volverá a generar la tabla. Esta matriz también requiere un área contigua de memoria por lo que, por ejemplo, si duplica desde una matriz de tamaños 32768 a una 65536, necesitará una porción de 256kB de memoria libre. Para evitar la asignación adicional y las penalizaciones de reajuste, solo use una tabla hash más grande desde el principio. También disminuirá la posibilidad de que no tenga un área contigua de memoria lo suficientemente grande como para caber en el mapa.


Probablemente necesite establecer el indicador -Xmx512m o algún número más grande cuando inicie Java. Creo que 64mb es el predeterminado.

Editado para agregar: después de averiguar cuánta memoria están usando realmente sus objetos con un generador de perfiles, es posible que desee buscar referencias débiles o referencias suaves para asegurarse de que no está accidentalmente reteniendo algo de su memoria como rehén del recolector de basura cuando ya no los estás usando.


Puede aumentar el tamaño máximo de almacenamiento dinámico pasando -Xmx128m (donde 128 es la cantidad de megabytes) a Java. No recuerdo el tamaño predeterminado, pero me parece que fue algo pequeño.

Puede verificar programáticamente cuánta memoria está disponible utilizando la clase Runtime .

// Get current size of heap in bytes long heapSize = Runtime.getRuntime().totalMemory(); // Get maximum size of heap in bytes. The heap cannot grow beyond this size. // Any attempt will result in an OutOfMemoryException. long heapMaxSize = Runtime.getRuntime().maxMemory(); // Get amount of free memory within the heap in bytes. This size will increase // after garbage collection and decrease as new objects are created. long heapFreeSize = Runtime.getRuntime().freeMemory();

(Ejemplo de Java Developers Almanac )

Esto también se aborda parcialmente en las Preguntas frecuentes sobre Java HotSpot VM y en la página de ajuste de Java 6 GC .



Pensamiento aleatorio: los depósitos de hash asociados con HashMap no son particularmente eficientes en la memoria. Es posible que desee probar TreeMap como alternativa y ver si todavía proporciona un rendimiento suficiente.