java.lang.OutOfMemoryError: Java heap space

se me presento el problema y es que en un programa donde se trae la informacion de los dos ultimos meses, donde son 1'250.000 registros cuando esa cantidad de datos pasan al arraylist
me salta la excepion Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
como es la manera correctar de manejar en un arraylist esa cantidad de informacion, la cual luego debo recorrerla y aplicarles unas formulas ???

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de Sr. Negativo

Coloca el código o..

el codigo

es un dto u un dao
el dao me case la consulta...
select * from consultadotabla;
esa consulta devuelve 1'250.000 esa info se carga en un dto
osea que en memoria van a ver esa cantidad de objectos
y la funcion devuelve un arraylist con todos esos objetos

Es mucha información

si por ejemplo, cada elemento de tu lista es de aprox 1kb entonces quieres decir que que estás cargando mas 1GB de datos a tu memoria. verifica cuanta disponible tiene tu VM antes de realizar el procesamiento, puedes apoyarte con: Runtime.getRuntime().freeMemory(). aunque toma en cuenta que hipoteticamente dije 1kb por elemento.

Lo mejor y mas sano será que proceses esa información en bloques mucho mas pequeños, es mas, hasta quizas los puedas procesar de forma concurrente.

java.daba.doo

cual seria la manera mas optima de manejar esos arrays tan grandes?? me dices que me traiga de bloques mas pequeños, pero recuerda que tengo que operar sobre todos los datos y aplicarle unas formulas.

Imagen de benek

Qué operaciones haces sobre

Qué operaciones haces sobre esa lista? Si te es posible puedes utilizar un arreglo y eso podría disminuir el problema. Sería bueno que analizaras ambos casos con alguna herramienta de profiling para ver la diferencia, puede ser notable.

Imagen de benek

Analizando más el escenario

Analizando más el escenario me surgen más preguntas... supongo que todos esos registros provienen de una base de datos? o de un servicio? En cuyo caso no sería más conveniente obtener la información en bloques?

También se me ocurre que pudieras implementar alguna solución como EhCache, que es una caché en la que puedes almacenar todos esos datos conforme los vayas obteniendo, y luego pedirle a EhCache los datos para las operaciones que necesites. EhCache se encarga de mantener algunos cuantos en memoria (en base al algoritmo de caché que definas) aunque la mayoría los persistirá a disco. Por un lado esto hará que la memoria que utilices sea bastante menor y a la vez tendrás acceso mucho más rápido a los datos porque es más rápido obtener información directamente de la memoria o el disco que de una base de datos o servicio.

Imagen de avefenix_x

Preguntate si tu solución es eficaz o eficiente

Uno de los problemas mas comunes en este tipo de casos es tu logica de negocio, entonces preguntate:

-Si es necesario traer toda la informacion de un solo golpe.
-Si toda la informacion que traes es util.

Te sugiero de que si haces calculos con los datos obtenidos solo traigas los que uses. Y si aun asi es pesado dale el trabajo a la base de datos, con algun procedimiento almacenado.

Saludos cordiales.

Puedes aplicar las formulas

Puedes aplicar las formulas en el SQL directamente?

select sum( columna ) from mundo where ...

Puede reducir el resultado mientras iteras?

int suma = 0
for ( e in resulset ) {  //   itera 1 millon de veces
    suma = suma + e.value
}

Depende de las fórmulas que quieras hacer puedes dejar que el RDBMS ejecute la fórmula o procesarlas mientras recorres el resultset

Otra opción es agregar mas memoria a tu JVM, pero si de todas formas te estás trayendo 1 gb de información desde la base de datos, vas a tener problemas de desempeño en la red.

Bloques mas pequeños

Supón que tu necesidad es realizar una suma de dos numeros que obtienes de DB.

long suma = 0;
 
for (int index = 0; ; i++) {
    //select * from... limit  {1000} offset {i * 1000}
    List<Any> blahblah= anyDao.selectBlah(1000, 1000 * i);

    for(blah : blahblah) {
        suma += sumameEste(blah.getA(), blah.getB())
    }
}

y creas un método que haga tus operaciones

... long sumameEste(long a, long b) {
    // Aquí puedes poner mas lógica de negocio, por ahora solo sumamos los números
    return a + b;
}

De esa forma estas cargando "pocos" registros a memoria y estas realizando las operaciones a cada uno de los elementos. En nuestro caso fué una súma sencilla, pero en éste ejemplo, podrás meter mas y mas... y mas lógica de negocio dependiendo de tu requerimiento