04 November 2010

CDI - A Major Risk Factor in Java EE 6

Update 18 Dec 2010: This post mainly relates to Glassfish 3.0.1. As of 3.1-b33, the situation has improved a lot.

Evaluating platforms and architectures for a new enterprise web application earlier this year, around March/April, scheduled to be released in 2011, I was bold enough to settle on Java EE 6.

In retrospective, I do not regret this decision, but I had to spend a significant amount of time analyzing and debugging platform problems. Based on this experience with Java EE 6, which may not be representative of course, the only advice I can give is: When Java EE 7 is released, wait a year or two before using it for production, unless you have the time and resources to act as beta tester.

The Java EE 6 specifications were released in December 2009, together with Glassfish v3, the first "production-quality release" of a Java EE 6 server. "Marketing release" would have been more appropriate, since going by the number of bugs in Glassfish 3.0, the main driving factor for the release date was quite obviously not a quality benchmark, but simply the need for Sun as the Java EE champion to release a working server simultaneously with the specifications.

The Problem


Java EE 6 was advertised as the lightest Java Enterprise Edition ever, with Contexts and Dependency Injection (CDI, JSR-299) as one of the most prominent innovations. However, for my project, CDI in the guise of Weld, its reference implementation, turned out to be the largest single source of problems. In addition to a number of functional bugs where injection would not work in certain scenarios, there are at least two severe memory issues:
  1. Weld causes memory leaks on application redeployment.
  2. Even in the first deployment of an application, Weld uses enormous amounts of memory.
The first issue had plagued us for months, and my team had grown accustomed to kill all Glassfish and Eclipse processes after three or four redeployments and to re-initialize their entire workspace, which was rather a pain.
Investigating the memory leaks using the Eclipse Memory Analyzer, I realized that Weld claimed almost 90 % of the heap memory of my application. Most of this was due to instances of org.jboss.weld.introspector.weld.jlr.WeldClassImpl, which appears to be a reflective model of a class inspected by the CDI BeanManager. Some instances of this class were as large as 1 MB.

I should add that our application uses Wicket for the web front end, injecting EJBs into custom Wicket components. Custom components inherit from Wicket base classes with a fairly deep inheritance hierarchy and a large number of methods. This accounts for the size of the reflective models.

Our usage of CDI was fairly basic, so it was just an hour's work to replace all @Inject annotations by Java EE 5 style @EJB annotations. After that, the heap usage of our application was reduced by 90 %, we no longer had any memory leaks, and we finally had a hassle-free edit-save-debug cycle in our development environment.

The figure of 90 % heap usage by Weld was observed on Glassfish 3.0.1, running on a 32-bit JVM on Windows XP.

This week, I retested the same application with Glassfish 3.1-b26 (using a newer version of Weld), running on a 64-bit JVM on Ubuntu 10.04. The result was not as drastic as before, but still about 50 % of my heap memory was consumed by Weld.

Alternatives


This article is mainly about problems with the CDI reference implementation Weld, not about CDI as such. But these implementation details have a ripple effect:
  • Glassfish 3.x still is the only certified Open Source Java EE 6 server. It uses a newer version of Weld in the current 3.1 promoted builds, but I can see no indication of the Weld issues being solved in time for the 3.1 release.
  • JBoss AS 6 is likely to be the next in line for full Java EE 6 compliance. However, JBoss also includes Weld, so you're bound to run into the same kind of problems on JBoss.
  • There are alternative CDI implementations, CanDI from Caucho and OpenWebBeans from Apache. Unfortunately, unlike JPA, there is no pluggable CDI provider interface that would allow you to simply replace Weld by some other CDI implementation on Glassfish or any other server.
  • I tried running my application on Resin 4.0.8 to see if CanDI was any better than Weld. However, Resin had bugs in the JPA and EJB areas and I was unable to deploy my application, let alone run and monitor it. (Resin only aims at supporting the Java EE 6 Web Profile, but that would have been sufficient for my case.)

Conclusion


All in all, if you want to work with Java EE 6, there is currently no way around Weld. And until Weld reaches production quality, the only way to build production quality applications on Java EE 6 is to avoid using CDI.

Free and Open Source Software is all about choice - so I would really love to see a pluggable CDI provider interface that would allow users to work with Glassfish and some CDI implementation other than Weld.

The second best solution might be to join forces from different CDI project to make Weld stable enough for production usage.

The third option would be another fully Java EE 6 compliant Open Source server (maybe Geronimo?) not including Weld.

No idea if any of these options is realistic, but this is on my wishlist for 2011.

11 comments:

Germán Escobar said...

Thanks for sharing. Yes, you're right. CDI is a great programming model but seems like the implementations can't keep up with it.

The part I dislike the most are the proxies everywhere and that the classpath has to be scanned every time on startup. However, I don´t think that the problem comes from the teams behind the implementations, but from the language itself, it is very limited for this type of solutions.

daemon said...

I tried CDI as well, but stopped using it before it had the chance to eat memory. The annotation and xml driven development model was a pain to use and I'm happy with Google Guice now.

The hole Java EE platform is not as consistent as it should be and there are still ancient things in it. I prefer to use a light weight approach with Servlet, JPA, and Guice, and other libraries if needed. Java EE is still a pain!

Gurkan Erdogdu said...

Did you checkout SIwpas, Simple Web Profile Application Server? We support CDI and other cool stuff of Java EE. We have also 24x7 support.

Checkout from http://mechsoft.com.tr/Mechsoft/software/en/acikkaynak/siwpas.html

phillips1021 said...

I thought it was possible to use Java EE 6 and Spring together and not have to use Weld for dependency injection.

http://blog.springsource.com/2010/10/19/spring-3-on-a-java-ee-6-server/

Harald Wellmann said...

@daemon: Not sure what you mean by "XML driven development model" - in Java EE 6 you can get by without using almost any XML at all.

In my project, all wiring is done by annotations or by convention. There is just a very simple web.xml for the Wicket servlet filter.

I agree that Java EE 6 is awfully bloated with stuff you might never need and with legacy solutions still supported for backward compatibility.

However, if you identify a small number of design patterns suitable to your project and stick with them, working with Java EE 6 is fairly easy.

I had some Java beginners (but with lots of PHP experience) on my team who were able to work productively very quickly, once we had set up some project guidelines.

Harald Wellmann said...

@Gurkan: Yes, I'd read about Siwpas, but I haven't tried it yet. Maybe you'd find more people willing to check it out if the project had a more pronounceable name ;-)

Harald Wellmann said...

@philips1021: Of course you can combine Java EE 6 and Spring, but why on earth should I include Spring in my application just to do dependency injection?

As I said, @EJB and @Resource injections do the job perfectly well in my project, without requiring any third-party libraries.

On Glassfish, you can also use the OSGi Declarative Services model for dependency injection.

Of course, you can use DS in any OSGi environment - it is easy to use, lightweight, no expensive classpath scanning and has IDE support in Eclipse.

Rohit Kelapure said...

Can you post your application on Github... We would like to get it running on WebSphere Application Server 8 that supports CDI via Open Web Beans

Harald Wellmann said...

@Rohit: I'm afraid I can't - the application is closed source.

Rohit Kelapure said...

Harald if you take the effort to move this to CDI we (CDI community) will take the effort to make this work for you ...

--Rohit Kelapure
Apache Open WebBeans committer

Stuart said...

I just ran a quick and dirty benchmark on the differences between the latest weld and weld 1.0.1 with a 5000 bean deployment, and the latest weld uses a third the memory and starts almost 5 times faster.

Also all known heap and permgen leaks have been fixed, and there have been big improvements in runtime performance as well.

Don't give up on weld just yet, it has come a long way since 1.0.1.