2011-04-30 10 views
306

Recibo este error en un programa que crea varios (cientos de miles) objetos HashMap con unas pocas (15-20) entradas de texto cada uno. Estas cadenas deben recopilarse todas (sin dividirse en cantidades más pequeñas) antes de enviarlas a una base de datos.java.lang.OutOfMemoryError: el límite superior del GC excedió

Según Sun, el error ocurre "si se gasta demasiado tiempo en la recolección de basura: si más del 98% del tiempo total se gasta en recolección de basura y se recupera menos del 2% del montón, un OutOfMemoryError será arrojado ".

Aparentemente, se podría utilizar la línea de comandos para pasar argumentos a la JVM para

  • Aumentar el tamaño del montón, a través de "-Xmx1024m" (o más), o
  • Desactivación de la comprobación de errores por completo, vía "-XX: -UseGCOverheadLimit".

El primer enfoque funciona bien, el segundo termina en otro java.lang.OutOfMemoryError, esta vez sobre el montón.

Entonces, pregunta: ¿hay alguna alternativa programática para esto, para el caso de uso particular (es decir, varios pequeños objetos HashMap)? Si utilizo el método HashMap clear(), por ejemplo, el problema desaparece, pero también lo hacen los datos almacenados en HashMap. :-)

El tema también se discute en un related topic in StackOverflow.

+1

Es posible que tenga que cambiar su algoritmo y el uso de algún tipo de estructura de datos más eficiente. ¿Puede decirnos qué algoritmo está tratando de implementar, que requiere tanta cantidad de HashMaps? – Ankur

+0

Acabo de leer archivos de texto muy grandes (cientos de miles de líneas cada uno), sobre los cuales no tengo control, es decir, no se pueden desglosar. Para cada línea de texto, se construye un HashMap que contiene unos pocos (en realidad alrededor de 10) pequeños valores de cadena, utilizando los mismos nombres de campo de base de datos una y otra vez. Idealmente, me gustaría poder leer todo el archivo antes de enviar los datos a la base de datos. – PNS

+1

Parece que leer todo el archivo antes de enviar los datos a la base de datos es una solución realmente deficiente ... de hecho, no funciona en absoluto, dentro de las limitaciones muy reales en la memoria disponible. ¿Por qué quieres hacer eso de todos modos? ¿Qué quiere decir con "usar los mismos nombres de campo de base de datos una y otra vez"? nombres de campo como claves o valores? Si los campos son claves, solo usa una matriz, donde el campo está IMPLICADO por su posición ... y si son valores, interélalos antes de agregarlos a los mapas. Ayudaría saber cuáles son los datos. Aclamaciones. Keith. – corlettk

Respuesta

154

básicamente estás quedando sin memoria para ejecutar el proceso sin problemas. Las opciones que vienen a la mente:

  1. especificar más memoria como usted ha mencionado, a intentar algo en el medio como -Xmx512m primera
  2. trabajar con lotes más pequeños de HashMap objetos para procesar a la vez si es posible
  3. Si usted tiene un montón de las cadenas duplicadas, utilice String.intern() en ellos antes de ponerlos en el HashMap
  4. utilizar el constructor HashMap(int initialCapacity, float loadFactor) para sintonizar para su caso
+1

Ya uso cerca de la capacidad inicial de HashMap, por lo que el programa es casi óptimo allí. – PNS

+2

Si funciona con más memoria ¿hay alguna razón para no ir con eso? En realidad, crecerá tan solo como sea necesario hasta su máximo si usa algo como '-Xms128m -Xmx1024m'. Parece la opción más simple. – WhiteFang34

+1

Sí, y supongo que el más rápido. Usé pasante() para algunos valores probablemente repetidos y el problema desapareció también. – PNS

11

Ummm ... ya sea que usted necesita:

  1. completamente, reflexionar sobre el algoritmo de & estructuras de datos, de tal manera que no necesita todas estas pequeñas HashMaps.

  2. Cree una fachada que le permita colocar esos HashMaps dentro y fuera de la memoria según sea necesario. Un simple LRU-caché podría ser solo el ticket.

  3. Subir la memoria disponible para la JVM. Si es necesario, incluso la compra de más RAM podría ser la solución más rápida y más barata si tiene la administración de la máquina que aloja a esta bestia. Una vez dicho esto, generalmente no soy partidario de las soluciones de "lanzar más hardware en eso", especialmente si se puede diseñar una solución algorítmica alternativa dentro de un marco de tiempo razonable. Si sigue tirando más hardware a cada uno de estos problemas, pronto se encontrará con la ley de rendimientos decrecientes.

¿Qué intentas hacer de todos modos?Sospecho que hay un mejor enfoque para su problema real.

+0

Ver mis comentarios arriba. El caso de uso es muy simple y estoy buscando una manera de procesar un archivo grande completo sin interrumpir en el medio del proceso. ¡Gracias! – PNS

5

Si está creando cientos de miles de mapas hash, probablemente esté utilizando mucho más de lo que realmente necesita; a menos que trabaje con archivos o gráficos de gran tamaño, el almacenamiento de datos simples no debe desbordar el límite de memoria de Java.

Debería tratar de repensar su algoritmo. En este caso, ofrecería más ayuda sobre ese tema, pero no puedo dar ninguna información hasta que proporcione más sobre el contexto del problema.

+0

Ver mis comentarios arriba. El caso de uso es muy simple y estoy buscando una manera de procesar un archivo grande completo sin interrumpir en el medio del proceso. ¡Gracias! – PNS

23

Para el registro, tuvimos el mismo problema hoy. Hemos fijado mediante el uso de esta opción:

-XX:-UseConcMarkSweepGC 

Al parecer, esta modificó la estrategia utilizada para la recolección de basura, lo que hizo que el problema desaparezca.

40

@takrl: El valor predeterminado para esta opción es:

java -XX:+UseConcMarkSweepGC 

lo que significa, esta opción no está activada por defecto. Así, cuando decimos que utilizó la opción "+XX:UseConcMarkSweepGC" Asumo que estaba utilizando esta sintaxis:

java -XX:+UseConcMarkSweepGC 

lo que significa que activaban de forma explícita esta opción. Para la configuración por defecto y sintaxis correctas de Java HotSpot VM Options @ este document

+0

En nuestro caso utilizando -XX: + UseConcMarkSweepGC redujo un poco el riesgo de error "OutOfMemoryError: GC overhead limit aboveeded" en situaciones de alta carga/alta presión de memoria, pero por otro lado usó más CPU, por lo que las solicitudes tomó un 5-10% más de tiempo para ejecutarse en situaciones de carga normal. – anre

0

Esto me ayudó a deshacerse de este error.This opción desactiva -XX: + DisableExplicitGC

9

Uso HashMap aplicación alternativa (Trove). El Java HashMap estándar tiene una sobrecarga de memoria> 12x. Uno puede leer los detalles here.

+6

\t net.sf.trove4j \t trove4j \t 3.0.3 Jeef

2

En caso de que el error:

"Internal compiler error: java.lang.OutOfMemoryError: GC overhead limit exceeded at java.lang.AbstractStringBuilder"

aumentar el espacio de montón de Java 2 GB es decir, -Xmx2g.

9

No almacene toda la estructura en la memoria mientras se espera para llegar a la final.

Escribir resultados intermedios en una tabla temporal en la base de datos en lugar de hashmaps. Funcionalmente, una tabla de base de datos es equivalente a una hashmap, es decir, ambos admiten acceso con clave a datos, pero la tabla no está tabla aquí en lugar de los hashmaps.

Si lo hace correctamente, su algoritmo ni siquiera debe notar el cambio - correctamente aquí significa usar una clase para representar la tabla, incluso dándole un método put (clave, valor) y get (clave) como un hashmap .

Cuando la tabla intermedia está completa, genere las declaraciones sql requeridas desde ella en lugar de desde la memoria.

8

El colector paralelo lanzará un OutOfMemoryError si se está utilizando demasiado tiempo en la recolección de basura. En particular, si más del 98% del tiempo total se gasta en la recolección de basura y se recupera menos del 2% del montón, se lanzará OutOfMemoryError. Esta función está diseñada para evitar que las aplicaciones se ejecuten durante un período de tiempo prolongado sin apenas progreso porque el montón es demasiado pequeño.Si es necesario, esta función se puede desactivar agregando la opción -XX:-UseGCOverheadLimit a la línea de comando.

+0

¿Dónde obtuvo esta información? Estoy interesado porque parece ser muy, muy correcto. lo encontré ... ---> http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html#par_gc.oom – MaasSql

59

Lo siguiente funcionó para mí. Sólo tiene que añadir el siguiente fragmento:

dexOptions { 
     javaMaxHeapSize "4g" 
} 

Para su build.gradle:

android { 
    compileSdkVersion 23 
    buildToolsVersion '23.0.1' 

    defaultConfig { 
     applicationId "yourpackage" 
     minSdkVersion 14 
     targetSdkVersion 23 
     versionCode 1 
     versionName "1.0" 

     multiDexEnabled true 
    } 

    buildTypes { 
     release { 
      minifyEnabled false 
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
     } 
    } 

    packagingOptions { 

    } 

    dexOptions { 
     javaMaxHeapSize "4g" 
    } 
} 
+0

Más detalles ver: http: // google .github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.DexOptions.html –

+0

Lo hice y todavía me estoy quedando sin espacio de montón. Android Studio 2.2.3. –

+0

¿cuál es su versión gradle? –

1

Para mi caso el aumento de la memoria utilizando -Xmx opción era la solución.

Tenía un archivo 10g leído en Java y cada vez que recibí el mismo error. Esto sucedió cuando el valor en la columna RES en el comando top alcanzó el valor establecido en la opción -Xmx. Luego, al aumentar la memoria con la opción -Xmx, todo salió bien.

Hubo otro punto también. Cuando configuré JAVA_OPTS o CATALINA_OPTS en mi cuenta de usuario y aumenté la cantidad de memoria, recibí el mismo error. Luego, imprimí el valor de esas variables de entorno en mi código, lo que me dio valores diferentes a los que establecí. La razón fue que Tomcat era la raíz de ese proceso y luego, como yo no era un su-doer, le pedí al administrador que aumentara la memoria en catalina.sh en Tomcat.

3

pérdidas de memoria Fix en su aplicación con ayuda de herramientas de perfil como Eclipse MAT o VisualVM

Con JDK 1.7.x o versiones posteriores, utilice G1GC, que gasta el 10% de la recolección de basura a diferencia del 2% en otros algoritmos de GC.

Además de establecer la pila de memoria con -Xms1g -Xmx2g, trate `

-XX:+UseG1GC 
-XX:G1HeapRegionSize=n, 
-XX:MaxGCPauseMillis=m, 
-XX:ParallelGCThreads=n, 
-XX:ConcGCThreads=n` 

Tenga una mirada en oracle artículo para ajuste fino de estos parámetros.

Algunos pregunta relacionada con G1GC SE:

Java 7 (JDK 7) garbage collection and documentation on G1

Java G1 garbage collection in production

Agressive garbage collector strategy

2

Es necesario aumentar el tamaño de la memoria en Jdeveloper ir a setDomainEnv.cmd.

set WLS_HOME=%WL_HOME%\server 
set XMS_SUN_64BIT=256 
set XMS_SUN_32BIT=256 
set XMX_SUN_64BIT=3072 
set XMX_SUN_32BIT=3072 
set XMS_JROCKIT_64BIT=256 
set XMS_JROCKIT_32BIT=256 
set XMX_JROCKIT_64BIT=1024 
set XMX_JROCKIT_32BIT=1024 

if "%JAVA_VENDOR%"=="Sun" (
    set WLS_MEM_ARGS_64BIT=-Xms256m -Xmx512m 
    set WLS_MEM_ARGS_32BIT=-Xms256m -Xmx512m 
) else (
    set WLS_MEM_ARGS_64BIT=-Xms512m -Xmx512m 
    set WLS_MEM_ARGS_32BIT=-Xms512m -Xmx512m 
) 
and 

set MEM_PERM_SIZE_64BIT=-XX:PermSize=256m 
set MEM_PERM_SIZE_32BIT=-XX:PermSize=256m 

if "%JAVA_USE_64BIT%"=="true" (
    set MEM_PERM_SIZE=%MEM_PERM_SIZE_64BIT% 

) else (
    set MEM_PERM_SIZE=%MEM_PERM_SIZE_32BIT% 
) 

set MEM_MAX_PERM_SIZE_64BIT=-XX:MaxPermSize=1024m 
set MEM_MAX_PERM_SIZE_32BIT=-XX:MaxPermSize=1024m 
5

Si tiene java8, y se puede utilizar el G1 del colector de basura, a continuación, ejecutar la aplicación con:

-XX:+UseG1GC -XX:+UseStringDeduplication 

Esto le dice al G1 para encontrar cadenas similares y tener una sola de ellos en la memoria, y los otros son solo un puntero a esa Cadena en memoria.

Esto es útil cuando tiene muchas cadenas repetidas. Esta solución puede funcionar o no y depende de cada aplicación.

Más información en:
https://blog.codecentric.de/en/2014/08/string-deduplication-new-feature-java-8-update-20-2/ http://java-performance.info/java-string-deduplication/

+0

Gracias George.Me ayudó a compilar Apache Camel: exportar MAVEN_OPTS = "- Xms3000m -Xmx3000m -XX: + UseG1GC -XX: + UseStringDeduplication" –

+0

Le damos la bienvenida, vigile el uso de la CPU porque el G1 GC es un poco más exigente. –

2

Para este uso por debajo de código en el archivo de aplicación en virtud de Gradle cierre androide.

dexOptions { javaMaxHeapSize "4G" }

Cuestiones relacionadas