Pregunta

En mi lugar donde trabajo, solía tener archivos con más de un millón de filas por archivo. Aunque la memoria del servidor tiene más de 10 GB con 8 GB para JVM, a veces el servidor se cuelga por unos momentos y ahoga las otras tareas.

Perfilé el código y descubrí que, mientras que el uso de memoria de lectura de archivos aumenta con frecuencia en Giga bytes (1GB a 3GB) y de repente vuelve a la normalidad. Parece que este uso frecuente de memoria alta y baja bloquea mis servidores. Por supuesto, esto se debió a la recolección de basura.

¿Qué API debo usar para leer los archivos para un mejor rendimiento?

Ahora estoy usando BufferedReader (nuevo FileReader (...)) para leer estos archivos CSV.

Proceso: ¿Cómo estoy leyendo el archivo?

  1. Leo los archivos línea por línea.
  2. Cada línea tiene pocas columnas. según los tipos los analizo de manera correspondiente (columna de costo en doble, columna de visita en int, columna de palabra clave en String, etc.).
  3. Empujo el contenido elegible (visita > 0) en un HashMap y finalmente borra ese Mapa al final de la tarea

Actualizar

Hago esta lectura de 30 o 31 archivos (datos de un mes) y almaceno los elegibles en un Mapa. Más tarde, este mapa se utiliza para obtener algunos culpables en diferentes tablas. Por lo tanto, leer es imprescindible y almacenar esos datos también es obligatorio. Aunque he cambiado la parte de HashMap a BerkeleyDB ahora, pero el problema al momento de leer el archivo es el mismo o incluso peor.

¿Fue útil?

Solución

BufferedReader es una de las dos mejores API para usar para esto. Si realmente tuvo problemas con la lectura de archivos, una alternativa podría ser usar el material en NIO para mapear en memoria sus archivos y luego leer el contenido directamente de la memoria.

Pero su problema no es con el lector. Su problema es que cada operación de lectura crea un montón de objetos nuevos, muy probablemente en las cosas que hace justo después de leer.

Debería considerar limpiar su procesamiento de entrada con el objetivo de reducir el número y / o tamaño de los objetos que cree, o simplemente deshacerse de los objetos más rápidamente una vez que ya no sea necesario. ¿Sería posible procesar su archivo una línea o un fragmento a la vez en lugar de inhalar todo en la memoria para su procesamiento?

Otra posibilidad sería jugar con la recolección de basura. Tienes dos mecanismos:

  • Llama explícitamente al recolector de basura de vez en cuando, digamos cada 10 segundos o cada 1000 líneas de entrada o algo así. Esto aumentará la cantidad de trabajo realizado por el GC, pero tomará menos tiempo para cada GC, su memoria no se hinchará tanto y, con suerte, habrá menos impacto en el resto del servidor.

  • Juega con las opciones del recolector de basura de la JVM. Estos difieren entre las JVM, pero java -X debería darle algunos consejos.

Actualización: Enfoque más prometedor:

¿Realmente necesita todo el conjunto de datos en la memoria al mismo tiempo para el procesamiento?

Otros consejos

  

Perfilé el código y descubrí que   mientras que el uso de la memoria de lectura de archivos aumenta en   Giga bytes con frecuencia (1GB a 3GB) y   entonces de repente vuelve a la normalidad. Eso   parece que este frecuente alto y bajo   La memoria utiliza cuelga mis servidores. De   Por supuesto, esto se debió a la basura   colección.

Usar BufferedReader (nuevo FileReader (...)) no causará eso.

Sospecho que el problema es que está leyendo las líneas / filas en una matriz o lista, procesándolas y luego descartando la matriz / lista. Esto hará que el uso de memoria aumente y luego disminuya nuevamente. Si este es el caso, puede reducir el uso de memoria procesando cada línea / fila a medida que lo lee.

EDITAR : Estamos de acuerdo en que el problema está relacionado con el espacio utilizado para representar el contenido del archivo en la memoria. Una alternativa a una gran tabla hash en memoria es volver a la antigua combinación de ordenamiento " enfoque que usamos cuando la memoria de la computadora se midió en Kbytes. (Supongo que el procesamiento está dominado por un paso en el que está haciendo una búsqueda con las teclas K para obtener la fila R asociada).

  1. Si es necesario, preprocese cada uno de los archivos de entrada para que puedan ordenarse en la clave K.

  2. Use una utilidad eficiente de clasificación de archivos para ordenar todos los archivos de entrada en orden en K. Desea usar una utilidad que utilizará un algoritmo clásico de clasificación de fusión. Esta voluntad divida cada archivo en fragmentos más pequeños que se puedan ordenar en la memoria, ordene los fragmentos, escríbalos en archivos temporales y luego combine los archivos temporales ordenados. La utilidad UNIX / Linux sort es una buena opción.

  3. Lea los archivos ordenados en paralelo, leyendo todas las filas relacionadas con cada valor clave de todos los archivos, procesándolos y luego pasando al siguiente valor clave.

En realidad, estoy un poco sorprendido de que usar BerkeleyDB no haya ayudado. Sin embargo, si la creación de perfiles le indica que la mayor parte del tiempo transcurrió en la construcción de la base de datos, puede acelerarla clasificando el archivo de entrada (¡como se muestra arriba!) En orden ascendente antes de construir la base de datos. (Al crear un índice grande basado en archivos, obtendrá un mejor rendimiento si las entradas se agregan en orden de clave).

Intente usar las siguientes opciones de vm para ajustar el gc (y realizar algunas impresiones gc):

-verbose:gc -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top