Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 339770 - org.eclipse.birt.core.script.ScriptEngineFactoryManager Memory Leak
Summary: org.eclipse.birt.core.script.ScriptEngineFactoryManager Memory Leak
Status: RESOLVED WORKSFORME
Alias: None
Product: z_Archived
Classification: Eclipse Foundation
Component: BIRT (show other bugs)
Version: 2.6.1   Edit
Hardware: PC Windows 7
: P3 critical with 27 votes (vote)
Target Milestone: Future   Edit
Assignee: Mingxia Wu CLA
QA Contact: Xiaoying Gu CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-03-11 18:01 EST by Scott Hamilton CLA
Modified: 2011-08-18 23:35 EDT (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Scott Hamilton CLA 2011-03-11 18:01:19 EST
I have a watchdog thread that times out a report by calling IRunAndRenderTask.cancel().  When BIRT exits, the report that had initiated the report (IRunAndRenderTask.run()) will call IRunAndRenderTask.close() in a finally block.

If the run() exited with a runtime exception or Error (e.g. OutOfMemoryError) I call task.cancel() just to be sure and in the finally block call close().

When a report runs and completes normally, memory is reclaimed/garbage collected normally.

If the report is canceled (which sometimes ends up throwing an IndexOutOfBoundsException from within BIRT, sometimes not) or if an exception occurs (e.g. I've also tried triggering a runtime exception in callback that the report makes into my code (a JDBC proxy) to force early termination - an attempt to work around bug #339446), I noticed that my heap was not returning to the level it was at before, especially if the report had gotten past the result set caching and into the rendering.

Profiling the result with YourKit, I found that org.eclipse.birt.core.script.ScriptEngineFactoryManager holds on to a set of factories, which hold on to its own cache of objects, which eventually hold on to objects that are initialized with a reference back to my IRunAndRenderTask, and these persist in the heap traceable back to this ScriptEngineFactoryManager.factories static Map.

So it looks like there is some code that normally cleans out these references which is NOT being executed when a runtime exception or Error (or perhaps also a normal "cancel()" event) causes the report to terminate abnormally.

You might argue that my approach to force the report termination with an exception is unusual, but I would argue back that this just happens to expose a problem that could occur in more normal circumstances such as NullPointerExceptions, OutOfMemoryErrors and the like.

In our situation, the amount of memory that is left uncollectible is on the order of 150meg.  All it takes is for a number of these to occur and the server soon becomes uselessly heap-exhausted.

As a temporary workaround, I catch exceptions coming out of the run() task and reflectively access this ScriptEngineFactoryManager.factories to clear it in that case.  I'm a little nervous about what side effects this might have (especially in a multi-threaded web application where others might be executing reports at the same time), but it is a risk that seems worth it rather than having to get woken up in the middle of the night to bounce an out of memory server.  I'd appreciate suggestions for better workarounds if there are any, or if someone can say why this might pose a problem, it might be good to know...
Comment 1 Lin Zhu CLA 2011-03-12 03:15:56 EST
Hamilton,

Thank you for so detailed info. We will take a look and try to fix it next release.

Lin
Comment 2 Jun Ouyang CLA 2011-08-18 03:11:05 EDT
Scott,

I tried to throw a runtime exception in method ILabelEventHandler.onCreate(), but failed to reproduce this problem, because report engine catches this exception immediately and add into error list, then releases all resources. 

The code in Engine is as following:
try
{
	ILabelInstance label = new LabelInstance( content, context,
					RunningState.CREATE );
	if ( handleScript( label, labelDesign.getOnCreate( ), context )
					.didRun( ) )
		return;
	ILabelEventHandler eh = getEventHandler( labelDesign, context );
	if ( eh != null )
		eh.onCreate( label, context.getReportContext( ) );
}
catch ( Exception e )
{
	addException( context, e, labelDesign.getHandle( ) );
}

All other event callbacks are invoked in the same way, which can ensure any runtime exceptions in event handler won't interrupt code following and all resources will be released correctly, unless an Error but not Exception is thrown. Was it an error in your case?

Cancelling report excution by IEngineTask.cancel() won't cause this problem either because the resources will also be released.

Could you please provide a sample design and its handler. It will be much helpful, thanks.
Comment 3 Scott Hamilton CLA 2011-08-18 07:43:58 EDT
It's been a while since I posted this, so the exact details are no longer in my memory.  However, we don't use many event callbacks - the error in question may have been coming from inside the OutputStream that BIRT was streaming its data into, or (as you probably know) the OutOfMemory error can erupt from just about any place in the code...
Comment 4 Jun Ouyang CLA 2011-08-18 23:35:32 EDT
Scott,

I go through the code of Engine tasks( Run/RunAndRender/Render) and confirmed that they actually release all resources in finally block. I can't figure out any cases that might cause the issue you described, so I resolve this issue as "works for me", please feel free to reopen it if you reproduce this issue.