A few days ago, one of the project managers here reported a huge problem: generating the same BIRT report on 50 different objects caused an OutOfMemory error. The client is waiting, it’s urgent, we needed it yesterday.. blablabla, you know how it goes.
So, we started profiling. Btw, I know nothing about BIRT. Using JProfiler, we found quite fast a static HashMap in the DataSource class containing the connection for a DataSession. So, the session was the key of the HashMap. Uhm.. smelly. We could see that this sessions were keeping everything in memory and the garbage collector could not release all the objects that were used to generate a report.
So we tracked back who added and removed the session objects into and from the HashMap. We downloaded the source, recompiled the DataSource class with a new Exception().printStackTrace() in the put method so we could see which class was responsible. Apparently, two sessions were added during one report of which one was removed properly. Allright, we’re getting closer!
The stacktrace of the unremoved session had some interesting packages mentioning mozilla and javascript.. Uhm. Let’s debug that stuff. It appears that suddenly a task is created, but we couldn’t see where it came from. So it had to be in the javascript. And indeed, looking at the reportdefinition, a script is executed, creating and executing a task, but… not closing it !!
We added the task.close(); and the sessions and all referenced objects could now be garbage collected. Woohoo!
The report definition now looks like this:
<report xmlns="http://www.eclipse.org/birt/2005/design" version="3.2.17" id="1">
<property name="createdBy">Eclipse BIRT Designer Version 2.3.2.r232_20090202 Build &<2.3.2.v20090218-0730></property>
<property name="units">in</property>
<method name="beforeRender"><![CDATA[curParams = new Packages.java.util.HashMap();
runnable = reportContext.getReportRunnable();
task = runnable.getReportEngine().createGetParameterDefinitionTask(runnable);
// get the names of all the parameters
paramRefs = task.getParameterDefns(false); //false = dont include parameter groups
// get the parameter that is tied to this table.
scalar = task.getParameterDefn("MaxKwh");
// bind the parameters to the query text
task.evaluateQuery(scalar.getName());
// get the values for this parameter from its DataSet
paramChoices = task.getSelectionList(scalar.getName());
maxValue = paramChoices.get(0).getValue();
reportContext.setGlobalVariable("maxKwh", maxValue);
task.close();]]>
</method>
Scripting is so evil..