Skip to main content

Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space

8 replies [Last post]
Anonymous

I'm developing an applet to show panoramas. Sometimes they can be larger than the memory allocated to the JVM on the client machine. In order to develop code to handle an OutOfMemoryError gracefully, I have restricted the memory allocated to the JVM while testing my applet in Eclipse (it's not an Eclipse problem).

While trying to load an image in the memory restricted JVM, I get an OutOfMemoryError from:

class sun.awt.image.ImageFetcher extends java.lang.Thread

Here is the stack trace:

* PanCyl2 [Java Applet]
sun.applet.AppletViewer at localhost:1946
Thread [Java2D Disposer] (Running)
Thread [AWT-Shutdown] (Running)
Thread [AWT-Windows] (Running)
Thread [AWT-EventQueue-0] (Running)
Thread [thread applet-pancyl.PanCylApplet.class] (Running)
Thread [DestroyJavaVM] (Running)
Thread [AWT-EventQueue-1] (Running)
Thread [Image Fetcher 0] (Suspended (exception OutOfMemoryError))
ImageFetcher.run() line: 149 [local variables unavailable]
Thread [Thread-3] (Running)
D:\jdk1.5\bin\javaw.exe (Jul 17, 2007 10:30:26 AM)

------

Here is the method in my applet where the OOME happens:

private boolean getImageParametersAndPixels()
{
System.err.println("getImageParametersAndPixels()...");
width = image.getWidth(canvas);
height = image.getHeight(canvas);
pixels = new int [width * height ];
pg = new PixelGrabber(image,0,0,width,height,true);
try
{

HAPPENS HERE->while (pg.grabPixels(100l) != true);
{
System.err.println("Grabbing()...");
pixelObject = pg.getPixels();
}
}
// catch (Throwable oome)
// {
// System.err.println("Throwable: getImageParametersAndPixels()...");
// oome.printStackTrace();
// return false;
// }
catch (OutOfMemoryError oome)
{
System.err.println("Throwable: getImageParametersAndPixels()...");
oome.printStackTrace();
return false;
}
catch(InterruptedException ie)
{
System.err.println("OOME: getImageParametersAndPixels()...");
return false;
}
pixels = (int [])pixelObject;
return true;
}

-----

PixelGrabber grabPixels() is supposed to throw an InterruptedException if --

/* @param ms the number of milliseconds to wait for the image pixels
* to arrive before timing out
* @return true if the pixels were successfully grabbed, false on
* abort, error or timeout
* @exception InterruptedException
* Another thread has interrupted this thread.
*/

But no InterruptedException is thrown. The OOME is raised in ImageFetcher.run() line 149 and my thread that is trying to grabPixels() is suspended. The applet is still alive and I could recover if I could detect the OOME.

But there is no way to detect that the OOME has occured.

--------

QUESTION: does anyone have an idea of how to handle this situation?

Shouldn't ImageFetcher.run() throw something? Like an OutOfMemoryError or something so that grabPixels() can throw an InterruptedException like the documentation says it will???

Ken

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Jim Graham

Hi Ken,

> QUESTION: does anyone have an idea of how to handle this situation?
>
> Shouldn't ImageFetcher.run() throw something? Like an OutOfMemoryError
> or something so that grabPixels() can throw an InterruptedException like
> the documentation says it will???

The ImageFetcher should capture any errors and pass on an ERROR
condition to all ImageConsumers. Is that happening here?

My suggestion would be to use ImageIO instead of the Toolkit image
facility for a couple of reasons:

- Getting pixels via PixelGrabber goes through some really indirect
mechanisms that should be slower than ImageIO (YMMV as we haven't
necessarily benchmarked your particular usage, but it would be a safe bet).

- ImageIO does the image loading in your own thread so you can catch
such errors and deal with them with a lot less fuss.

- ImageIO will deliver you one single copy of the image - when you use
PixelGrabber and Toolkit images then you run the risk of the Toolkit
image storing the pixels itself and then also feeding them to your
PixelGrabber which would double the amount of memory needed and the
memory used for the Toolkit image is harder to control since it is
managed by another mechanism (hint: Image.flush() should get rid of it,
but be careful not to trigger it to reload the image data again in the
future). To give you an idea - image.getWidth(canvas) is enough to
trigger the Toolkit image to load and store its own version of the image.

- Also, if the Toolkit image is loading its own version of the image in
addition to the data it delivers to your PixelGrabber, that is even more
of a performance loss.

- (BTW, PixelGrabber can get the W/H itself and create an array for you
if you use the PixelGrabber(image, 0, 0, -1, -1, forceRGB) constructor.
That would avoid the "second copy loaded for the Toolkit image"
problem...)

Have you tried ImageIO?

...jim

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Ken Warner

Hi Jim,

You raise some interesting points. The reason I used Toolkit was because of the simplicity of the code that I needed to write. I wrote an early version of my applet using ImageIO and BufferedImage. The code ended up being pretty complicated. Right now I use Toolkit; grab the pixels; flush the image and do all my image manipulations on the raw one dimensional byte array returned by the PixelGrabber.

I have quite a lot of functionality wrapped up in a pretty small package. Less than 20k and it has it's own nearest neighbor; bilinear; and bicubic interpolators all built into the projection from cylindrical to rectilinear viewport.

Take a look:

http://pancyl.com/

And I'm so far along in my development cycle that recoding for ImageIO (which is really good stuf - no slight intended) that recoding for ImageIO would set me back a month or more.

The thing is, PixelGrabber implements the ImageConsumer interface. And the doc's say an InterruptedException is supposed to be raised -- I quote the doc's in my previous message -- but it's not. That's the problem. If it was -- I'd be home free. The thread I do the PixelGrabbing from is just suspended. All other threads are still up and running.

To be clear -- the image is downloaded; the image has been decoded by Toolkit; when I try to grab the pixels, my thread is suspended by ImageFetcher.

The way I fetch the image from the server is I just download it as a byte array then give it to Toolkit to decode. Really simple code. It all works with reasonable speed considering the usual constraints on downloading a 1 meg file over the internet.

Another handicap is that I don't have the source to

class sun.awt.image.ImageFetcher extends java.lang.Thread

It's one of those mystery classes. If I had source, a workaround might pop out at me.

Do you have the source to sun.awt.image.ImageFetcher or know where I can get it???

Ken

Jim Graham wrote:
> Hi Ken,
>
>> QUESTION: does anyone have an idea of how to handle this situation?
>>
>> Shouldn't ImageFetcher.run() throw something? Like an OutOfMemoryError
>> or something so that grabPixels() can throw an InterruptedException like
>> the documentation says it will???
>
>
> The ImageFetcher should capture any errors and pass on an ERROR
> condition to all ImageConsumers. Is that happening here?
>
> My suggestion would be to use ImageIO instead of the Toolkit image
> facility for a couple of reasons:
>
> - Getting pixels via PixelGrabber goes through some really indirect
> mechanisms that should be slower than ImageIO (YMMV as we haven't
> necessarily benchmarked your particular usage, but it would be a safe bet).
>
> - ImageIO does the image loading in your own thread so you can catch
> such errors and deal with them with a lot less fuss.
>
> - ImageIO will deliver you one single copy of the image - when you use
> PixelGrabber and Toolkit images then you run the risk of the Toolkit
> image storing the pixels itself and then also feeding them to your
> PixelGrabber which would double the amount of memory needed and the
> memory used for the Toolkit image is harder to control since it is
> managed by another mechanism (hint: Image.flush() should get rid of it,
> but be careful not to trigger it to reload the image data again in the
> future). To give you an idea - image.getWidth(canvas) is enough to
> trigger the Toolkit image to load and store its own version of the image.
>
> - Also, if the Toolkit image is loading its own version of the image in
> addition to the data it delivers to your PixelGrabber, that is even more
> of a performance loss.
>
> - (BTW, PixelGrabber can get the W/H itself and create an array for you
> if you use the PixelGrabber(image, 0, 0, -1, -1, forceRGB) constructor.
> That would avoid the "second copy loaded for the Toolkit image"
> problem...)
>
> Have you tried ImageIO?
>
> ...jim
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".
>
>

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Jim Graham

Hi Ken,

Ken Warner wrote:
> Hi Jim,
>
> You raise some interesting points. The reason I used Toolkit was
> because of the simplicity of the code that I needed to write. I wrote
> an early version of my applet using ImageIO and BufferedImage. The code
> ended up being pretty complicated. Right now I use Toolkit; grab the
> pixels; flush the image and do all my image manipulations on the raw one
> dimensional byte array returned by the PixelGrabber.

Your code used a one dimensional int array - I'm presuming that was what
you meant above.

You could do something very similar with ImageIO, but you might have to
use some of the more specific API calls. You can request an image to be
read into a specific format of BufferedImage using the ImageReadParam,
but you need to get an ImageReader directly, query it for the default
ImageReadParam, and then change/set the destination. It's a few more
lines than ImageIO.read(File), but it gets you an image in the default
ARGB space that your PixelGrabber was using. Then you just need to dig
out the int array from the BufferedImage:

WritableRaster wr = bimg.getRaster();
DataBufferInt dbi = (DataBufferInt) wr.getDataBuffer();
int pixels[] = dbi.getData();
// voila - pixels in a one dimensional array

Note that the cast will cause a problem if you aren't using a
TYPE_INT_ARGB or TYPE_INT_RGB BufferedImage since the various formats
use different types of DataBuffer objects under the hood.

> And I'm so far along in my development cycle that recoding for ImageIO
> (which is really good stuf - no slight intended) that recoding for
> ImageIO would set me back a month or more.

If you only need to grab an int array from an image then ImageIO should
be able to do that with just a few changes to your image loading code,
unless I'm missing something...?

> The thing is, PixelGrabber implements the ImageConsumer interface. And
> the doc's say an InterruptedException is supposed to be raised -- I
> quote the doc's in my previous message -- but it's not. That's the
> problem. If it was -- I'd be home free. The thread I do the
> PixelGrabbing from is just suspended. All other threads are still up
> and running.

The docs say that InterruptedException is raised if "this" thread is
interrupted (i.e. via an explicit call to Thread.interrupt()) which
isn't what is happening.

On the other hand, the method should return if the producer calls
imageComplete an ERROR flag. Unfortunately, the stack trace shows that
the ImageFetcher thread is in a suspended state due to the OOM
condition. In other words, the OOM was so severe that the thread could
not even continue executing. It's hard for the thread to notify you of
anything if the VM suspends it.

This goes back to what another person said about OOM being one of those
conditions that you can only cross your fingers and hope to survive.
Some code may be able to wind its way out of an OOM if it catches the
OOM in just the right place. Other code may be in the middle of a
situation that it cannot back out of. And in this case it looks like
even the VM can't figure out how to back out of the situation since it
couldn't even make space for some local variables so it suspended the
Thread.

> To be clear -- the image is downloaded; the image has been decoded by
> Toolkit; when I try to grab the pixels, my thread is suspended by
> ImageFetcher.

It sounds like you are getting double copies of everything then since
the grabbed pixels are separate storage from the version that was loaded
by the Toolkit for use with drawImage. That's going to exacerbate your
memory usage and make OOM more likely to happen.

Also, your thread is not "suspended by ImageFetcher" per se - it is in a
wait() call waiting to be notified that the appropriate data has been
delivered - the data coming from the ImageFetcher thread. Once the
ImageFetcher gets hit with an OOM, it can get summarily suspended as in
the current case and cannot notify you of anything. That's not an easy
situation to code for.

There is a call to grabPixels that does take a timeout if you want to
use that to avoid permanent suspension of the calling thread, but at
that point your VM is so wedged that it had to suspend a system thread
so I don't think your program would return to too much functionality.

> The way I fetch the image from the server is I just download it as a
> byte array then give it to Toolkit to decode. Really simple code. It
> all works with reasonable speed considering the usual constraints on
> downloading a 1 meg file over the internet.

Giving it to ImageIO would only be a few more lines of code, not a
complete rewrite of your package.

> Another handicap is that I don't have the source to
>
> class sun.awt.image.ImageFetcher extends java.lang.Thread
> It's one of those mystery classes. If I had source, a workaround might
> pop out at me.

I'm afraid that this line from your stack trace:

Thread [Image Fetcher 0] (Suspended (exception OutOfMemoryError))
ImageFetcher.run() line: 149 [local variables unavailable]

pretty much says "System wedged, please exit and try again"...

> Do you have the source to sun.awt.image.ImageFetcher or know where I can
> get it???

The sources are published as part of the OpenJDK project:

http://openjdk.java.net/

...jim

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Jim Graham

I have a couple more items that may affect OOM when using the Toolkit
image code...

Try using the Toolkit.createImage(...) variants instead of the
Toolkit.getImage(...) variants. The latter getImage calls all keep a
copy of the image that was returned in an internal hashmap in case they
get a future request for an image loaded from the same location. I
think you said you were pre-loading the data into byte arrays before
making a Toolkit image out of it in which case you are probably already
using the createImage(byte array) call and there will be no implicit
caching for that call.

If you drop your reference to the original Image (and to the
PixelGrabber that you construct with it), then call the Image.flush()
method on the object before you drop your reference to it. This will
cause the system to immediately flush all of the pixel data for the
Toolkit version of the image - which won't affect your PixelGrabber copy
at all - and the memory can be reclaimed more aggressively.

It might be a good idea to call System.gc() after you flush the image
and drop your reference(s) to it to be more proactive about keeping
memory cleaned up.

Finally, the code:

int w = image.getWidth(canvas);
int h = image.getHeight(canvas);
int p[] = new int[w * h];
PixelGrabber pg = new PixelGrabber(image, 0, 0, w, h, p);
// ... pg.grabPixels...

is only reliable if you force the loading of the image before you call
this code. If you do not preload the image then the calls to getWidth
and getHeight might return -1 meaning "the data isn't loaded yet". If
you load the image from a local byte array of image data then the data
might be decoded fast enough to be ready by the time you call getWidth
and getHeight, but there is still potential for a race condition there.
Finally, those calls to getWidth/Height(canvas) both cause the Toolkit
image to populate itself with pixel data that you will likely never use
since you only care about the copy in the PixelGrabber.

The following code:

PixelGrabber pg = new PixelGrabber(image, 0, 0, -1, -1, true);
// ...pg.grabPixels...
int w = pg.getWidth();
int h = pg.getHeight();
int p[] = (int[]) pg.getPixels();

will do the same thing, but never trigger the Toolkit image to load its
internal copy of the data. The PG copy of the data will be the only one
created. The width and height will be obtained by the PixelGrabber from
the ImageConsumer stream and it will create an int[] array if you tell
it to force the RGB colormodel with the last parameter "true".

Hopefully with one or more of these suggestions you can reduce your
memory consumption and avoid OOM conditions for much longer...

...jim

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Ken Warner

Hi Jim,

In the last 12 hours, I've found out a lot of interesting stuff. I wish I could sit in your office and explain it all. Email has limits...

But to summarize -- I solved the conundrum of how to gracefully exit from an OOME.

I put the call to pg.grabPixels(long n) in a separate thread -- pgThread -- then I loop on pgThread.isAlive(). pgThread() stays alive even though grabPixels() is wait()'ing for ImageFetcher to notify() it. However, the PixelGrabber object is still alive and well and ImageFetcher does set the state to IMAGEERROR in pg,ImageComplete(). Each time through the pgThread.isAlive() loop, I simply check the error state. If there is an error, I notify both the PixelGrabber and the pgThread.

It all comes unwedged at that point and I am able to catch Throwables in various places and eventually end up right where I want to.

I am sending you the complete source to MY class -- in a separate email -- that does my image fetching and image creation and pixel grabbing so you can have a better idea of what I did. It's messy. But I want you to see the whole thing before I clean it up.

Here is an EXECUTION trace -- not a stack trace. I printout information so I can watch the execution.

APPLET
PanCyl v0.1h
INIT
getImageBytes()...
makeImageFromToolkit()...
START
loadImage(images/Room.jpg)
run()...
getImageBytes()...
makeImageFromToolkit()...
getImageParametersAndPixels()...
pgThread alive...
status = 0
ts = RUNNABLE
Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space
pgThread alive...
status = 192
ts = RUNNABLE
isAlive: image fetch aborted or errored
(1)Throwable: getImageParametersAndPixels()...
Grabbing Pixels Failed...
(2)Throwable: getImageParametersAndPixels()...
STOP
DESTROY...

I suspect that the reason why you wrote MediaTracker is the same reason why PixelGrabber will wedge. I suspect that this has to do more with the native threads than the Java JVM.

Ken

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Ernst de Haan

Ken,

Question is whether OOMEs can be handled gracefully at all. I'm not
an expert in this field, but it may be that the JVM enters an error
state as soon as this error is triggerred. Note that the JVM self may
be unable to get sufficient resources for its basic operations.

But then again, I'm not an expert. But I have witnessed heated
discussions on this topic, a few years ago.

So my suggestion is to first do some research to confirm OOMEs can in
theory be handled gracefully.

Cheers,

Ernst

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Ken Warner

Hi Ernst,

Like most things Java, there are two viewpoints. I often hear that OOME is the last gasp of the JVM and that all attempts at recovery are futile -- sort of like being Boorg'ed.

I don't believe that. It's an urban myth...

In fact, I have a small applet that exhausts all memory and lives to tell the tale.

http://pancyl.com/memoryhelp.htm

What I think is that the guys who developed the memory allocation scheme for Java were smart enough to check to see if there was enough memory for the next allocation *BEFORE* they allocated it. My reasoning is that even though the next memory allocation may indeed require more than all available memory, before that last chunk is allocated, there is still enough to recover by dumping some objects (o = null;) and/or System.gc().

That's what my little applet does. It waits for the OOME to be thrown and then dumps what it has allocated and runs System.gc().

The problem I described is that ImageFetcher just suspends and doesn't signal the OOME so that I can take steps to recover.

Ken

Ernst de Haan wrote:
> Ken,
>
>
> Question is whether OOMEs can be handled gracefully at all. I'm not an
> expert in this field, but it may be that the JVM enters an error state
> as soon as this error is triggerred. Note that the JVM self may be
> unable to get sufficient resources for its basic operations.
>
> But then again, I'm not an expert. But I have witnessed heated
> discussions on this topic, a few years ago.
>
> So my suggestion is to first do some research to confirm OOMEs can in
> theory be handled gracefully.
>
> Cheers,
>
>
> Ernst
>
>

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

erickson
Offline
Joined: 2004-07-07

Hi Ken,

I'm not an expert on Java2D at all, but if I understand your problem, the OOME is raised by some background thread in ImageFetcher, so your application thread can never catch it.

One thing you could look into is getting the ImageFetcher's background thread into a java.lang.ThreadGroup that you provide. This could be tricky in itself, but if you can do it, you may find that this error is being thrown all the way up to ThreadGroup.uncaughtException(Thread,Throwable)

By default, this just prints the stack trace, but you can extend ThreadGroup to provide your own logic to signal the application thread, and get ImageFetcher to use your custom ThreadGroup.

Good luck,

Doug