Error "java.lang.OutOfMemoryError: Java heap space" al usar Objetos DAO

Saludos...

Tengo una inquietud, y es que estoy por primera vez adentrándome al desarrollo de aplicaciones de tres capas con java.

Bueno, no estoy al tanto de algunos términos que están relacionados con esta tecnología de la arquitectura del software,
pero trataré de ser lo más claro posible.

Según lo que he investigado, se recomienda en el desarrollo de aplicaciones, desacoplar las clases que se encargan de
la presentación visual de los datos con las clases que manejan la base de datos.

Según lo que he estudiado, implicaría hacer clases que extraigan los datos de la base y los guarde en objetos que
simulan cada instancia, ser un registro de la tabla manipulada. A que conlleva?... a que si necesito extraer 100 registros,
deberé crear 100 objetos que contenga cada registro de dicha tabla.

Aquí es donde se pone peliaguda la cosa y es que yo manejo una base de datos de más de 1 millón de registros y necesito
extraer y presentar en una tabla aproximadamente 400k registros y como ya sabrán esto implicaría "fabricar" 400k objetos...
y es allí donde me genera el error mencionado en el título de este post.

Yo en lo personal no le veo práctico este tipo de arquitectura ya que presentaría no solamente este tipo de problemas sinó que
implicaría extraer demasiada información y colocarla en objetos para posteriormente volver a extraer dicha información de los
objetos para colocarlas en el frontend de la aplicación...

Que piensan al respecto, hay alternativas para aplicar arquitectura de software que ayude a la mantenibilidad y la re-utilización
de código en estos casos? o alguien ha optado por aplicar algún tipo de método adicional o propio para hacer frente a este
inconveniente??

les agradezco sus comentarios
Me parece práctico sin embargo,

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.

Depende de lo que quieras

Depende de lo que quieras hacer con la información.

Muchas veces los datos pueden ser reducidos para obtener información que sea entendible, por ejemplo, no tiene caso listar 400k ventas para saber la cantidad total vendida. Lo que necesitas es una suma esa operación se puede hacer mejor en la base de datos ( )

En otras ocasiones se puede paginar para mostrar al usuario una cantidad de información que pueda manejar, por ejemplo gmail guarda millones de correos pero solo muestra unas decenas al usuario a la vez y pagina el resto ( y claro solo le muestra el usuario las que le pertenecen )

Y en otras ocasiones ( no se me ocurre ningún ejemplo ahorita ) necesitas efectivamente tener toda esa información en memoria, lo mejor es diseñar una estructura de datos eficiente ( un registro que tenga solamente lo necesario ) y pues simplemente usar más RAM, si se necesita se necesita y punto, si necesitas tener en memoria al mismo tiempo 16 gb de datos y solo tienes 8 gb, pues habrá que ir a la tienda a comprar los otros 8 gb.

Definitivamente hay que pensar dependiendo del escenario como usar eficientemente la memoria.

En otro escenario se puede comprimir o codificar la información de tal manera que use menos espacio pero si al final se debe de volver a transforma a su representación original.

Así que, la respuesta ( jajaj ya volví a mi etapa de consultor ) depende, ¿Que quieres hacer? A mi me suena que lo que necesitas es un resumen de los datos y no todos los datos a la vez.

Aplicativo en Powerbuilder con BD en Sybase

El asunto está en que yo tengo que mantener un aplicativo desarrollado en Powerbuilder 8.0 con acceso a base de datos Sybase Sql Anywere 9.0,
cabe mencionar que dicha plataforma utiliza una herramienta patentada que se llama DataWindow, el cual encapsula la mayor parte de la programación de acceso a base de datos...

Un punto en contra es que la base de datos se vuelve lenta a medida que va aumentando su tamaño, actualmente en el periodo 2014, la base maneja casi 400k facturas y el datawindow lo que hace es cargar todos esos registros y ya teniendo los datos en memoria, despliega en el cliente o interfaz de usuario la información concerniente a cada factura.

He estado pensando en migrar de plataforma de Powerbuilder a Java EE como aplicativo WEB, ya que consume alrededor de 12 a 15 segundos en cargar los 400K, hice una migración de base de datos a FireBird y al cargar los datos y presentarlos en pantalla no pasa de tres segundos...

He pensado que al usar la arquitectura de 3 capas en el nuevo app, quizás debería de hacer la extracción de los registros uno por uno y así mismo ir presentando los datos en pantalla registro, por registro, pero quizás eso consuma muchos recursos, no lo he pensado concretamente.

Mi idea era hacer lo mismo en Java cargar todos los registros (400k) y luego desplagarlos en interfaz correspondiente, aunque solo se desplegará en pantalla un registro a la vez. Al aplicar la arquitectura de 3 capas, solo llega a generar 160k registros y me manda el mensaje de error mencionado como título del post. Esto lo hago en un PC de 32GB de RAM, intel corei7 4770 disco SSD de 240GB. pero el Netbeans el cual trabajo me indica que no puedo especificar una memoria heap máxima de 1gb. por lo que al ver la ejecución de la prueba con el profile me precisamente muestra que cuando colapsa el 1Gb de memoria asignada se salta la ejecución...

He estado pensando en no utilizar los objeto DTO para guardar los datos de las tablas, más bien que el objeto DAO devuelva un resultset, al Facade y este inyecte los datos requeridos al frontend, otra que pensé es no eliminar los objetos DTO y que el DAO extraiga el registro solicitado uno por uno. Se supone que cuando el usuario haga click en la flecha derecha, se va mostrar en patalla el siguiente registro, si da click en la flecha izquierda el DAO extrae el registro anterior, genera con un Objeto Factory el DTO y lo pasa al Facade para que este inyecto el registro consultado al Frontend...y asi

saludos amigo OscarRyz

Si es para mostrarlo al

Si es para mostrarlo al usuario puedes cargar 100 en vez de 400,000, quizá hasta menos y usar paginación. Si le presentas 400,000 registros al usuario no va a poder usarlos todos a la vez.

Toma gmail como ejemplo:

- Tienes una tabla con el titulo de registros y algunos datos para identificarlo como: fecha, usuario etc.
- Carga 50 mensajes a la vez ( a pesar de que yo tengo 3gb de mensajes).
- Al dar click sobre un registro se abre inmediatamente el detalle.
- En la part superior tienes un paginador que te dice en donde estás y cuantas páginas hay.

Intenta hacer una aplicación con ese diseño ( puede ser en web, puede ser en escritorio) También hay que incluir una mecanismo de búsqueda, no es práctico tener que buscar con el scroll una factura entre 400,000.

Para aumentar el tamaño de la memoria en Java usa la opción -Xmx aquí esta la doc:

Comprendo bien la idea...

Saludos y gracias por tus recomendaciones...

Comprendo bien la idea que explicas y me parece que es la mejor alternativa que tengo hasta ahora.

Sin embargo, es muy probable que de todas maneras me pidan el despliegue de la factura (cabecera + detalle) de manera directa, ya tienen más de 5 años trabajando de ese modo, además de navegar secuencialmente por las facturas, existe un botón de busqueda por # de factura para dirigirse directamente a un un documento específico.

Si termina siendo ese el caso, no sería práctico el cargar 400k registros con el uso de la arquitectura de 3 capas, mencionas algo de usar paginación, tiene algo que ver el uso del método "setFetchSize(int numeroRegistros)" de la clase PreparedStatement?? o tiene que ver algo con instrucciones SQL??

Según tengo entendido, setFetchSize permite cargar "n" cantidad de registros cada vez que se haga una llamada al método resultset.next(); Es decir, si hago un SQL que mande a consultar 400k registros y especifico setFetchSize(1000); se cargará en el resultset 1000 registros y cuando se haga un .next(); si es necesario cargará otros 1000 registros cuando se haya acabado los anteriores 1000.

No he probado esto y me interesa saber si funciona con el resultset.previous();

Veo también que de este modo no cabe el uso de objetos para el almacenamiento de cada registro de la tabla consultada. Incluso me doy cuenta que en casos como este tocaría hacer las consultas registro por registro como lo menciono anteriormente...

Voy investigar un poco más con respecto a este asunto...

Con respecto a lo del aumento de memoria, aplico los comandos especificados en oracle y me indica el mensaje:

 

Saludos amigo...

No es una cuestión de capas,

No es una cuestión de capas, sino de usabilidad. Un usuario va a poder ver cuando mucho 100 registros a la vez y va a ignorar los 399,900 restantes, luego querra ver otros 100.

La paginación es pues..... abre gmail, se ve algo como esto:

En la parte superior dice 1 - 100 of 1,889 <> que significa que esta mostrando los registros ( correos ) del 1 al 100 de un total de 1,889 y luego las flechas sirven para ir a la siguiente pagina (101 - 200 ) De esa forma gmail no esta cargando mis 1,889 correos lo cual consumiría muchos recursos, sino que solo carga 100 y me da una forma de obtener otros 100.

Eso es paginación. Una forma de implementarlo es como lo describes, pero eso dejaría el cursor abierto innecesariamente, lo mejor es acotar la sentencia para que solo traiga los primeros 100.

 
Donde n es el id del primer registro y m el que 100 registros después.

Sobre el error de Xmx es muy raro que no puedas pedir 1gb de memoria en una máquina que tiene 32, quizá tienes una versión vieja, o de 32 bits (y aún así deberías poder pedir mucho más de 1gb)

entendí bien tu sugerencia...

entendí bien tu sugerencia, y como mencioné voy a implementar el sistema de paginación que me muestras en el pantallazo para presentar grandes cantidades de registros.

Mi inquietud muy aparte es el caso hipotético de que tuviera que mostrar solo 1 registro a la vez, de manera detallada de un conjunto total de 400k o más registros existentes, como se implementaría aquello... Es así como menciono que no sería práctico del uso de una arquitectura de 3 capas a base de Objetos DAO's y DTO's ,etc. ya que eso colapsaría el sistema...

Ya mencioné algunas maneras en que he pensado implementarlo, incluso el de abstraer una arquitectura ideada por mí para poder superar los inconvenientes que se dan al tener que trabajar con grandes cantidades de información... Inclusive te cuento que ya lo he hecho, pero con una arquitectura de 2 capas, inyectando directamente los resultsets a los objetos visuales en la interfaz del usuario. Pues he creado objetos que heredan de clases Swing y los he adaptado para que de manera automatica el formulario obtenga un resultset y éste pueda desplegar la información requerida en dichos objetos, y con un Scroll al cual no tengo que programar, sino que hago que genere el codigo automaticamente, me permite navegar por los registros y tengo sistemas de búsquedas directas también implementadas... y lo he manejado con resultsets de dos millones de registros, los cuales solo demora la primera vez en cargarlos y mostrarlos unos 5 segundos, los cuales todavia los veo demorado pero para lo que debería de manejar realmente la aplicación es suficiente por ahora.

Mi inquietud está en el manejo de grandes cantidades de datos en una arquitectura de 3 capas,

En lo referente a la memoria pues tengo un Windows 7 64bits de 32GB de ram con el sistema XMP activado, etc.

le cambie el comando solo en su forma y me sale ahora esto:

 

solo me ejecuta si le especifico hasta 1Gb
sera configurarle algo más será
le puse el -Xms64m por cierto

saludos

¡Psst! Oye, Freddy...

 

¡Psst! Oye, Freddy... Tal vez quieras ver esto si es que quieres implementar el sistema de paginación. ;-)

~~~

Lo que estoy leyendo es que

Lo que estoy leyendo es que dices: "2 capas sirve para muchos datos, 3 capas no es escalable" bueno NO como lo estás haciendo que es sin entender como es que tus datos están viajando de un lugar a otro.

Lo más probable ( y digo "probable" porque no tengo idea como esté tu código ) es que en las dos capas estás trayendo datos según los vas mostrando:

 

Y cuando los dejas de mostrar el garbage collector los elimina. Luego entonces no estás cargando todos los datos, sino que estás cargando solo unos cuantos a la vez ( lo cual esta bien ), a costa de tener abierta la conexión teniendo una conexión por cliente ( que funciona bien si tienes N clientes donde N es el número máximo de conexiones abiertas a la base de datos for forma concurrente )

Y cuando estás haciendo "en tres capas" estás vaciando todo a registros y usando toda la memoria a la vez:

 

Lo cual significa que estás tomando toda la base de datos, pasarla por la red hacia la memoria de otra máquina ( el servidor ) y luego pasarla de nuevo por la red la memoria de otra máquina ( el cliente ) de un solo golpe y te sorprende que se acabe la memoria.

Sobre si el netbeans "te deja" o no usar más de un 1gb de memoria es otro tema. Si tienes una máquina con 32gb de RAM pero no puedes hacer que Java use más que 1gb entonces tienes efectivamente un sistema de 1gb con 31 desperdiciandose.

gracias por tu sugerencia...

gracias por tu sugerencia amigo jpaul, aunque veo que esta orientado al uso del sybase, el cual no estoy implementando nada nuevo, puede que me sirva de base para posteriores aplicaciones en java, voy a analizarlo... :)

saludos

gracias amigo OscarRyz por tu paciencia...

Analizando el asunto, veo que no me he explicado bien con algunos puntos...

Primero, soy sincero en decirlo, no aplico arquitectura de software de capa alguna, aunque según los conceptos que he investigado, lo que yo hago sería aplicar arquitectura de software de dos capas, o al menos eso entiendo (modelo - aplicación/presentación) por un lado base de datos (modelo) y por otro lado en las mismas clases que manejan la interfaz de usuario agrego métodos que controlan el acceso a la base de datos (aplicación/cliente).

Segundo, administro una base de datos Sybase SQL Anywere, y tengo un par de aplicativos en Java que accesa a dicha base de datos, que realiza ciertos procesos. Como mencioné arriba, hago un SQL y guardo en un resulset y directamente muestro en pantalla, no uso objetos DAO ni nada por el estilo, jala de un lado y muestra datos por las mismas.

Tercero, estos aplicativos son de escritorio y NO son aplicativos web.

Cuarto, el sistema administrativo principal que se usa, esta hecho en powerbuilder, no lo hice yo, sino otra persona, y este señor lo que hace es con un datawindow jalar todos los registros de las facturas que son 400k y lo muestra en pantalla registro por registro en detalle, al abrir la ventana se demora como 15 segundos mínimo ya que tiene que cargar semejante cantidad de registros. Pero vuelvo a recalcar no lo hice yo sino otra persona y lo hizo en Powerbuilder de hace como 10 años atras...

Ahora bien, lo que yo mencionaba sobre la arquitectura de 3 capas (modelo - aplicación - cliente), siendo este un aplicativo web o de escritorio, ¿como se haría para manejar grandes cantidades de registros ( como los 400k registros que maneja el app de powerbuilder )?

...... Por un lado ya mencionaste que se podría implementar un sistema de paginación como la de los correos electronicos como Gmail. Excelente es una buena sugerencia y la voy a aplicar.

...... Por otro lado, sin embargo, qué pasaría si se requiere presentar las facturas, una por una, detalladamente; y que se tenga acceso también a todas las demás facturas. (Ahí está el detalle), esto tomando en cuenta las tecnologias de la arquitectura del software recomendadas a utilizar como lo es el encapsular el acceso de datos y separarlo de frontend...

gracias amigo, hasta aqui escribo ya que debo de tenerte aburrido, jejeje

saludos.

Uso de buenas prácticas

 

Si no te he malinterpretado, tú quieres cargar en memoria 400k registros e ir consultando uno por uno. Sin embargo, tal cosa no se considera una buena práctica.

La sugerencia de Oscar —la cual yo también recomiendo ampliamente— implica que tengas que dividir tu consulta actual en una serie de consultas pequeñas.

Por ejemplo, tú puedes ① cargar al principio una página con los primeros 100 registros, ya sea con todos los campos o únicamente unos pocos en una especie de sumario. Si eliges mostrar sólo el sumario... y quisieras ver un registro completo en particular, entonces ② consultas la información completa de ese registro, semejante a Gmail cuando te muestra el mensaje completo cuando seleccionas uno de la lista (trae el mensaje completo desde el servidor). Como podrás darte cuenta, es necesario consultar varias veces la base de datos.

Respecto a la arquitectura de tres capas... te recomiendo: Why should I use an MVC pattern?.

~~~

Uso de buenas prácticas 2 ...

amigo jpaul, gracias por tu interés en cuanto a mi inquietud...

Pues, realmente no quiero cargar en memoria 400k registros para después mostrarlos uno por uno...Eso es un proceso que ya se hace en un aplicativo el cual tengo que administrar y que no es mio, a la vez que está desarrollado en Powerbuilder con base de datos Sybase.

Lo que yo estoy planteando es una situación hipotética. Cómo se tendría que estructurar un aplicativo, o qué técnicas de arquitectura de software habría que implementar, para desarrollar una interfaz de usuario en el cual se despliegan las facturas ingresadas durante un periodo de tiempo X, en el cual se requiere que el despliegue sea 1 factura a la vez y dicha factura desplegada sea mostrada en detalle, es decir, mostrar la cabecera con los datos principales y el detalle de la venta con sus respectivos totales. A la vez se requiere navegar secuencialmente por todas las facturas existentes y lógicamente tener a disposición sistemas de búsquedas directas.

En este caso ya no aplicaría la sugerencia que ustedes me dan de utilizar un sistema de paginación de registros...

Por otro lado tampoco sería práctico el cargar en memoria 400k registros para después desplegarlos. En el aplicativo de powerbuider que administro, se lleva como mínimo 15 segundos en cargar esos registros...

En la arquitectura MVC o de 3 capas, como se lo haría, se extraería registro por registro?? o internamente se aplicaría el sistema de paginación, extraer X cantidad de registros cada vez?? me parece lo más lógico y es lo que tu mencionas aunque no los deplegaría en sumario ya que ese no es el requerimiento solicitado por el usuario final...