lunes, 22 de diciembre de 2008

Comparativa de implementaciones de JPA: Toplink, EclipseLink, Hibernate y OpenJPA

Este artículo es una respuesta a la falta de información existente en internet en lo que se refiere a las diferencias entre las 4 implementaciones más conocidas de la llamada Java Persistence API (JPA de ahora en adelante).
He realizado 4 pruebas exactamente iguales, una con cada implementación de JPA. Las diferencias entre las diferentes implementaciones han sido muy considerables, dando como ganador indiscutible a Hibernate, y como perdedor a Eclipselink. Puedes leer las conclusiones detalladas más abajo, si quieres.

También tenéis disponible este otro artículo acerca del uso de GWT, Axis y JPA de manera simultánea, porque tiene sus particularidades y merece la pena dedicarle uno completo. El actual, en cambio, se referirá exclusivamente a las diferencias en rendimiento detectadas en las diferentes implementaciones más utilizadas de JPA: Toplink, EclipseLink, Hibernate y OpenJPA.

Descripción del proyecto y las pruebas

El servidor con que se han hecho las pruebas (un Tomcat 5.5) ha ejecutado de manera concurrente dos proyectos desarrollados con Eclipse consistentes en lo siguiente:
  • Un proyecto que no tiene pantallas y sí acceso a base de datos (Oracle), tanto de inserción como de búsqueda. Utiliza JPA para el acceso a bbdd y Axis para la exposición de un web service. 
  • Un proyecto que sí tiene pantallas. Utiliza igualmente JPA para acceder a Oracle  (sólo búsqueda) y el Google Web Toolkit para la parte cliente (GWT de ahora en adelante), haciendo uso intensivo de ajax.
Para lanzar carga se ha utilizado:
  • JMeter para el servicio web, configurado con 5 hilos que lanzan 100 peticiones cada uno. Se configuró un tiempo de subida de 30 segundos. En total, por tanto, 500 llamadas , habiendo siempre 5 simultáneas.
  • Google Chrome para hacer peticiones, abriendo 10 ventanas simultáneas. Hay que tener en cuenta que cada ventana dispone de varias peticiones Ajax de actualización que se ejecutan cada 5 segundos (se ha forzado un tiempo tan corto la prueba). Es decir, cada 5 segundos se ejecutan 10x3=30 peticiones ajax simultáneas. No he utilizado JMeter para lanzar la carga web debido a que no hubiera replicado la parte ajax de la página, que es lo que realmente introduce carga en el servidor.
  • Para la medición de la memoria, he utilizado el JConsole, incluido en el jdk 1.6.
  • Para el mínimo tratamiento de imágenes (cortar, fundamentalmente) he utilizado el GIMP 2.
La prueba da comienzo con el Tomcat recién arrancado. Seguidamente se abren las 10 ventanas del navegador. Después se espera un minuto aproximadamente, para posteriormente lanzar los 5 hilos con JMeter, que lanzarán las 500 peticiones. Al acabar las peticiones se dejan pasar unos 5 minutos y después se activa a mano el Garbage Collector desde Jconsole. Esto último se hace para no tener que esperar (cuestión logística, no tengo tanto tiempo :-).

Diferencias en el rendimiento

Pues bien, éste ha sido el comportamiento de las diferentes implementaciones, de peor a mejor.

Eclipselink

Eclipselink parece ser la siguiente versión de Toplink, que Oracle habría donado a la comunidad de Eclipse. Hay que tener en cuenta que Toplink lleva mucho tiempo funcionando, no como implementación JPA, sino como capa de acceso a dato. Oracle añadió capas para que fuera accesible vía JPA, exactamente igual que hicieron los de Hibernate.
Supongo, por tanto, que Eclipselink es aún una implementación muy nueva que aún tiene que madurar, exactamente igual que Openjpa.

Eclipselink dio el peor rendimiento durante las pruebas, con mucha diferencia. La memoria ocupada no hizo más que aumentar desde el principio de la prueba, poniendo de manifiesto que hay algún tipo de memory leak sin controlar en dicha implementación.
En concreto, el máximo de memoria utilizada (60 mb) triplica al utilizado por Hibernate o Toplink (poco más de 20 mb).

La memoria ocupada al final de la prueba, aun después de forzado el recolector de basura (GC), quedó en 43 mb, marcando también un record máximo, y casi triplicando la ocupada por Hibernate o Toplink (13 y 15 mb respectivamente).

Los tiempos de respuesta, por otro lado, fueron el doble que los de Hibernate o Toplink.

Apache Openjpa

Openjpa es la respuesta de Apache en lo que se refiere a JPA, y es la implementación por defecto en Gerónimo, el servidor de aplicaciones de Apache que incluye JPA y EJB3. En cualquier caso, me sorprende que la gente de Apache haya liberado algo tan aparentemente inmaduro.

Openjpa se comportó algo mejor que Eclipselink, sobre todo en lo que se refiere a los memory leaks, pero no tanto como para considerarla una alternativa viable para su uso en producción.
Por un lado, se observa el típico comportamiento de diente de sierra del Garbage Collector a lo largo de la prueba (aunque no de manera tan correcta como en Hibernate o Toplink), y por otro lado la memoria ocupada en el heap al final de la prueba es menor que la implementación que mostró peor rendimiento (32 mb frente a los 43 mb de Eclipselink), pero el doble que en el caso de Hibernate o Toplink.
Otro dato a tener en cuenta sobre Openjpa es que no informa en el log del servidor cuando arranca, al contrario que todas las demás implementaciones. Esto al menos ocurre con la configuración por defecto.

Toplink Essentials

Toplink Essentials es un subconjunto gratuito del software java de acceso a datos de Oracle. Dicha empresa le añadió varias capas de abstracción para hacerlo compatible con JPA, pero la base continúa siendo la original, estando por tanto bastante maduro.

El comportamiento de Toplink se acerca mucho al de Hibernate. La diferencia en memoria ocupada es poco apreciable. Por un lado, el máximo a lo largo de la prueba fueron unos 25 mb, al igual que Hibernate, y el total ocupado al finalizar fue de 15 mb, 2 megas por encima de Hibernate. Si tenemos en cuenta la enorme diferencia con Eclipselink y Openjpa, podemos decir que el rendimiento es muy similar al de Hibernate, y es desde luego una alternativa viable para su uso en producción.

Hibernate

Hibernate es probablemente el ORM más utilizado, el que más influencia tuvo en la especificación de EJB3, y la implementación de JPA que viene incluida en las últimas versiones de JBoss. La gente de Hibernate, al igual que la de Oracle, incluyó varias capas de abstracción en su software para hacerlo compatible con JPA. Lo bueno es que el software que hay detrás de esas capas está sobradamente probado, al haber muchísima gente que lo utiliza ya en entornos de produccción, lo que lo hace bastante maduro. Esto, entiendo yo, explicaría el excepcional rendimiento que dio durante la prueba.

El rendimiento en Hibernate fue muy superior al de sus competidores, a excepción de Toplink. Hibernate necesitó 3 veces menos de cantidad de memoria para funcionar que Eclipselink y Openjpa, y al terminar la prueba estaba ocupando 3 veces menos también que dichas implementaciones.
Aunque su rendimiento se asemeja al de Toplink, lo supera en memoria ocupada al final de la prueba (13 mb de Hibernate frente a 15 de Toplink).
El diente de sierra debido al funcionamiento del recolector de basura es perfectamente apreciable en la imagen, pero funciona ligeramente mejor que para Toplink cuando se deja de lanzar carga. Toplink queda con unos 24 mb ocupados, frente a los 20 de Hibernate. Lo cierto es que cuando se fuerza la ejecución del garbage collector, la diferencia entre ambos se reduce a 2 mb. Según la carga que vaya a recibir una aplicación, esa diferencia podría llegar a ser importante.

Conclusiones
  • El ganador de esta competición de implementaciones de JPA es, sin lugar a dudas, Hibernate. Fue la implementación que mejor resultado dio en cuanto a consumo de memoria durante y después de la prueba y en cuanto a tiempos de respuesta. Toplink le sigue muy de cerca en rendimiento.
  • En cuanto al perdedor, Eclipselink tuvo el peor comportamiento de todos, seguido muy de cerca por Openjpa. En mi humilde opinión, ninguna de las 2 implementaciones parece lo suficientemente madura como para ser utilizada en producción, al menos con la configuración por defecto. El consumo de memoria sin freno y los aparentes memory leaks llevarán a dejar colgado al servidor de aplicaciones cuando se ocupe toda la memoria heap disponible.
  • Mi recomendación, hoy por hoy, por madurez y seguridad en el rendimiento, es sin duda para Hibernate, teniendo en cuenta que las pruebas se han hecho con Toplink Essentials, un subconjunto de la implementación de JPA de Oracle, que si se quiere completa hay que pagar por ella. Hibernate, en cambio, es completamente libre y gratuita.

23 comentarios:

Javier dijo...

Excelente comparativa, felicidades!

Pai dijo...

Muchas Gracias por exponer tu trabajo.

Guillermo dijo...

Mil Gracias y excelente tu aporte y por compartirlo.

ANTUANF1 dijo...

Bravo un gran análisis

Ezequiel dijo...

Muy buen análisis!

Saludos

andy dijo...

Una comparativa buena intentaria separar la capa de base de datos de la capa de web para tener una conclusion que solo analisa el ORM. Por ejemplo, si analisas las operaciones tipicas en tu aplicacion y disenas pruebas asi, sin la necesidad tener el codigo de Axis, GWT etc..

Las pruebas y configuraciones no son publicos, por eso cualquier proyecto puede decir que has configurado mal al input a su software. Hazlo publico para evitarlo.

el que lleva el cotarro dijo...

Andy, tienes razón. Una comparativa formal pondría el código a disposición del todo el mundo, de forma que las pruebas fueran reproducibles, y habría intentado aislar lo que se quiere probar, en este caso JPA, de forma que no hubiera "cara" en forma de tecnología cliente, como GWT.
El tema es que este artículo no es resultado de unas pruebas planificadas como tales. Simplemente estaba trabajando en un proyecto de desarrollo en el que usábamos JPA y se me ocurrió cambiar Toplink por Openjpa, para ver qué tal iba. Comprobé que el Tomcat se caía, y eso me preocupó. Así que me puse a monitorizar al servidor para ver lo que estaba pasando, y comprobé que parecía haber memory leaks importantes en Openjpa. Y ya puestos, decidí hacer pruebas con otras implementaciones, para ver cómo iba la cosa.
Por tanto, esto no son pruebas formales, repetibles por todo el mundo y aisladas. Son el resultado del esfuerzo de alguien que necesitaba una implementación JPA fiable para su proyecto y que pensó que esta información podía ser útil a la comunidad, sin más. No tengo más aspiraciones.
De hecho, es más de lo que encontré en internet. No encontré comparativas, ni formales ni no formales, ni reproducibles ni no reproducibles.
Agradezco, en cualquier caso, tu crítica e incluso en cierto modo la comparto, como ves.

Un saludo.

Eduardo Bregaida dijo...

Parabéns pelo artigo.

Fonsito dijo...

En cuanto a que EclipseLink no está completa... yo diría que no sólo está más completa que TL 11g, sino que TL 11g y sucesivos se van nutriendo y completando con Eclipse Link.

Cuando un gigante dona código a la comunidad, luego vive de ellos (IBM WSStudio de Eclipse, SJSAS de Netbeans, TopLink de EclipseLink...).

Cuando tienen un producto y se les hace muy grande (y poco rentable de mantener) lo donan a la comunidad, la comunidad lo mejora y lo publica, y ellos a cambio lo cogen, le añaden 4 chorradas (o ninguna), y lo venden de forma comercial (o al menos el soporte).

Ah, por cierto... que la implementación de referencia de JPA 2.0 será EclipseLink, y no Toplink (y mucho menos Hibernate, que viene de una empresa, y esta si que te puede dejar vendido si le da por cambiar las licencias en plan Spring).

Anónimo dijo...

Nenhuma comparação com DataNucleus: http://www.datanucleus.org/ antigo JPOX ?

Anónimo dijo...

Excelente!! Aunque quedó en el tintero saber qué tan complejo es el uso de cada una, y que tan grande puede llegar a ser la curva de aprendizaje.

6€€K M€L0M@N0 dijo...

Estuve probando recientemente EclipseLink, sabiendo que es la supuesta evolución de TopLink pero después de tu excelente post, ni pensarlo en utilizarlo en mis proyectos. Normalmente utilizo TopLink, pero creo que voy a entrar más de lleno con Hibernate. Gracias por el aporte.

Alejandro dijo...

El trabajo es interesante. ¿Utilizaste algún pool de conexiones como por ejemplo c3po? , no estoy seguro si ya viene incorporado con hibernate. Gracias por el aporte.

xantyago dijo...

Hola Alejandro. No, no utilicé ningún pool de conexiones, aunque, efectivamente, Hibernate trae incorporado el suyo.

Un saludo.

DIEGO F dijo...

hola xantiago

muy bueno este estudio y analisis de los resultados.

soy nuevo en hibernate y en todo lo relacionado a la capa de persistencia de una aplicacion.

en la actualidad tengo que hacer una trabajo similar con iBatis,

quisiera saber que herramientas utilizo para hacer el estudio ademas de de la metodologia para el desarrollo de las mismas.

gracias por su ayuda

xantyago dijo...

Hola Diego.

Te ruego revises el apartado "Descripción del proyecto y las pruebas". Ahí describo en detalle tanto las herramientas utilizadas como el método seguido.

Un saludo.

Ascari Romo dijo...

¿por qué en el tutorial en Ingles Hibernate tiene las peores cifras en general y EclipseLink tiene muy buen rendimiento, pero en este tutorial en español resulta que eclipselink tiene el peor rendimiento y hibernate el mejor????

xantyago dijo...

Hola Ascari.
No sé por qué dices que Hibernate tiene las peores cifras en el artículo en inglés. No es así. Hibernate consiguió hacer el mayor número de inserts (4 veces más que Eclipselink y 24 veces más que OpenJPA) y el número de queries ejecutados no difiere mucho de los demás. Así se indica en las conclusiones. De hecho, considero que es de los que tuvo el mejor comportamiento.

Sandy Noa dijo...

He estado leyendo en Internet sobre el tema JPA y recién ahora me encuentro este blog el cual me ha parecido bastante bueno, quisiera preguntar algo, esta es mi situación: tengo un entorno de trabajo con Eclipse Indigo en el cual he creado un proyecto EJB (con capacidad para JPA[Hibernate]) y por otro lado cuento con una BD Oracle 11g. En dicha BD ya se encuentran creadas todas mis tablas, secuencias, indices, etc... y lo que intento hacer es generar las entidades a partir de las tablas, he utilizado las librerías de JPA que ofrece JBossAS7.1.0 pues es aqui donde finalmente se desplegara el proyecto. El driver de conexión a la BD Oracle y sus respectivas configuraciones están correctos, el problema fundamental radica en que cuando intento generar las clases de entidad desde el IDE, existen algunas que no se generan. Quisiera saber si tienen alguna idea de por que ocurre esto o si pudieran describir como lograr generar las entidades (mediante alguna utilidad de Eclipse, manualmente, etc...)

Santiago Rodríguez dijo...

Hola Sandy. En este enlace puedes ver cómo crear entidades JPA a partir de tablas de una base de datos con eclipse. Comprobarás que es bastante sencillo. Un saludo.

Anónimo dijo...

hola, has realizado una prueba actual? en este año? 2013? saludos.

Alejandra dijo...

Gracias! Ha sido muy útil tu información.

Victor Martin dijo...

q buen articulo! gracias por la info