Limit BIRT heap usage to avoid OutOfMemoryError

Update on 18th april : this article has entered “Best March 2011 DevShare Contribution” on BIRT Exchange. Please vote here if you have found it usefull. Thanks.

Eclipse BIRT is a very commonly used platform for generating many kind of reports. It provides core reporting features such as report layout, data access and scripting. Specially, its report layout editor combined with native eclipse extensions such as WTP makes it a powerful tool for designing and generating email based news letter.

I manage last weeks to use this platform for generating such a news letter for Trailplans – a free application for discovering and sharing walks, bike or urban trails through points of interest – and come to some limitations and – more interesting – some workarounds that I found it’d be interesting to share.

Report Engine is greedy !

The Report Engine section of BIRT documentation mentions a sample on how to cleanly initialize/finalize BIRT engine. Applying this sample allows you to quickly run out and generate a report. Nice.

However, here’s a fact : when it comes to use this sample for generating dozens of reports in a loop, the memory used by BIRT grows till the saturation of the JVM HeapSize, ending out with a beautiful OutOfMemoryError.

My setup : BIRT 2.6.1 and JDK 1.6.

A workaround

A simple trick – but maybe not so obvious because not mentionned on any BIRT related forum – is to cache the BIRT engine loaded artifacts that are the EngineConfig, IReportEngine and the IReportRunnable. To do that, just use a static initialization block into your launching class containing your main() method.


static IReportEngine engine = null;
static EngineConfig config = null;
static IReportRunnable design = null;


static {
  config = new EngineConfig();
  config.setEngineHome(PropertiesManager.getProperties()
.getProperty("birt.home"));
  config.setResourcePath(Thread.currentThread().getContextClassLoader()
.getResource(".//report").getPath());


  try {
    Platform.startup(config);
    IReportEngineFactory factory = (IReportEngineFactory) Platform.createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY);
    engine = factory.createReportEngine(config);


    // Open the report design
    design = engine.openReportDesign(Thread.currentThread()
.getContextClassLoader()
.getResource(".//report//" + PropertiesManager.getProperties()
.getProperty("birt.report.name")).openStream());
  } catch (BirtException bex) {
    // Do whatever you want...
  }
}

That’s it ! Your report engine is now ready to be called thousand of times without any memory leak ! Just concentrate your report processing related stuffs into another static method that you can call from within a loop. Here’s an illustration :


public static void executeMailingReport(InfoPerson infoPerson) throws BirtException {
  IRunAndRenderTask task = engine.createRunAndRenderTask(design);
  // Initialize person_id parameter
  task.setParameterValue("person_id", infoPerson.getId());
  // Initialize Locale used by person
  task.setLocale(new Locale(infoPerson.getLocale()));
  task.validateParameters();
  // Prepare render options ...
  [...]
  task.setRenderOption(options);
  // Run !
  task.run();
}

Let me know if it has been helpful.

Advertisements

11 thoughts on “Limit BIRT heap usage to avoid OutOfMemoryError

  1. What if i have hundreds of report designs? Will it be enough to have a static map “reportName:String”->”reportDesign:IReportRunnable”?

    And what if my report design changes (is updated) while program execution?

    Is it significant that engine and design are static or will it be enough to have only one instance of them?

    1. Hello Jan,

      in my use case, I only load one report design. Loading hundred of designs does not seem to be reasonnable within a single JVM with a ‘standard’ 512MB to 1GB heap size. However I do not have experienced such a case …

      You are right about the fact that engine and design are static is not significant. The important part here is to ensure that there’s only one instance loaded.

      The main problem of loading many instances other the time comes from the destroy() and shutdown() methods on engine and platform that does not seem to free all the memory.

  2. I am having this issue, but I am not sure how to implement the static block you are talking about.

    Basically I have the viewer app running in tomcat, and about 10 reports that are viewed through a browser.

    Where do I put in the static block you speak of? In the reports themselves? Or somewhere else?

    1. Hi Bob,

      sorry but I’ve never encountered such issue when using the viewer app. The workaround I’ve posted is only for reports executed from a main java app.

  3. In most cases, it is not advisable to make the report design static. In your case, since it is a single report design, that is fine. But the theory is, you should only ever have a single ReportEngine per JVM instance, and you would have multiple Run and/or Render tasks. You should not be creating a ReportEngine for every runnable instance of your report. If you do, you will consume all your memory, and performance will suffer for it.

    Your also not closing your RunAndRender task in the above example. Once the run is complete, you should call task.close() to free up any resources used by the task. And int he above example, since only the parameters change, you can keep re-using the same task, just put task.setParameterValues and task.run in your loop.

    But you are right, there isn’t a lot of documentation on best practices for implementing the BIRT REAPI, and the ins and outs of how it should be implemented, just code samples of actually invoking it.

    Regards,
    John

  4. Hi,

    I am new to BIRT. I have the same question as Bob. How does one implement the workaround if report is view through using the BIRT Report Viewer?

    Thank you,
    Grace

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s