adam bien's blog

Is it worth to use POJOs instead of EJB 3 in terms of performance? (with results, source and load scripts) 📎

I hear from time to time funny statements like "EJB 3 are too heavyweight", or "POJOs are lightweight". If I have the chance I ask directly how the term "lightweight" is defined - and get really funny (mostly inconsistent) answers. This remembers me somehow the answers I get for questions like: "What you actually meant by serviceorientation?" or even better "What do you mean by Web 2.0?".

Lastly I was asked "...It would be interesting to see if the same web app would be faster if you only developed a web-project using JSF, Derby and TomCat as Webserver..." (instead of Glassfish with EJB 3, JSF and Derby). This request made me really curious - and I built two independent projects. An EAR "EJBLoadTest" which consists of an Servlet and to cascaded EJB 3.

public class NumberGenerator extends HttpServlet {
    @EJB
    private NumberGeneratorFacadeLocal numberGeneratorFacadeBean;
  
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            out.println(numberGeneratorFacadeBean.getNumber());
        } finally {
            out.close();
        }
    }
 }

 With two local Session Beans. The first one fetches the millis, calls the second one and computes the result. The business logic is dumb, but it avoids GC optimizations.

@Local
public interface NumberGeneratorFacadeLocal {

    public long getNumber();
   
}

@Stateless
public class NumberGeneratorFacadeBean implements NumberGeneratorFacadeLocal {
  @EJB
  private MillisProviderLocal millisProviderBean;
   
  public long getNumber(){
      return System.nanoTime() - millisProviderBean.getMillis();
  }
 
}

The second bean is simple as well.  

package com.abien.loadtest.ejb;

import javax.ejb.Stateless;

@Local
public interface MillisProviderLocal {

    public long getMillis();
   
}

@Stateless
public class MillisProviderBean implements MillisProviderLocal {
   
    public long getMillis(){
        return System.currentTimeMillis();
    }
}
 

I introduced the two beans, to maximize the overhead between the "POJOs" and EJB3. In simple applications there would be only one layer - and so less overhead.

The POJO application is identical. Instead of EJBs I used POJOs, and instead of an EAR, a WAR.

The POJO servlet comes with an additional "init" method. Because dependency injection doesn't work for POJOs (=classes without the @EJB annotation), the POJO creation was factored out in a init method:

public class NumberGenerator extends HttpServlet {
   private NumberGeneratorFacadeLocal facade;
  
    public void init(){
        this.facade = new NumberGeneratorFacadeBean();
    }

The same story in the facade -  the DI was replaced with an constructor call:

public class NumberGeneratorFacadeBean implements NumberGeneratorFacadeLocal {
  private MillisProviderLocal millisProviderBean;
 
  public NumberGeneratorFacadeBean(){
      this.millisProviderBean = new MillisProviderBean();
  }
   
  public long getNumber(){
      return System.nanoTime() - millisProviderBean.getMillis();
  }
 }

 

For the load tests I used JMeter. I repeated the tests several times. The load generator and the Glassfish v2 were on the same machine. This differs a little bit from real world - but is perfectly suitable to show the EJB 3 overhead.

The results: 

  • EJB3: The throughput of the EJB 3 solution was 2391 transactions/second. The slowest method call took 7 milliseconds. The everage wasn't measurable. Please keep in mind that in every request two session beans were involved - so the overhead is doubled.
  • POJO: The throughput of the POJO solution was 2562 requests/second (request - there are no transactions here). The slowest method call took 10 ms.

The difference is 171 requests / seconds, or 6.6% (therefore 3.3% for a single session bean).

Conclusion:

The dfference is less than expected (I expected an overhead over 10%)... However the POJO sample only works for idempotent / transient use cases. It wouldn't work for highly concurrent, database applications -  I only created one instance for servlet - not request.... In that case, you will have to synchronize the access to the shared state e.g. with transactions or "instances per request". In that case a plain web container wouldn't be the simplest solution.

However the EJB 3 solution would work even with database and JMS without modification... The funny story here - the POJO solution required some more lines of code. :-).

I checked the projects (POJOLoadTest, EJB3LoadTest), the load script, as well as the results (as screenshots) into http://p4j5.dev.java.net. I used plain Netbeans 6.0 for this purpose - feel free to reexecute the load test again - and share the results :-).