OutOfMemoryError on OSF1, part-II

G

Guest

All:

I've done some more digging into the OOME I'm getting on
an OSF1 platform, and here's an even simplier example that'll
fail every time. I would be much obliged if someone can post
a change to the Display.java code below that will fix things
up. (Roedy's reference link on Ratpacking was helpful, but just
slowed up receipt of the OOME by a little.)

% uname -a
OSF1 sys1 V5.1 1885 alpha

% java -version
java version "1.4.1"
Java(TM) 2 Runtime Environment, Standard Edition
Fast VM (build 1.4.1-2, native threads, mixed mode, 06/18/2003-17:51)

% java -Xverbosegc:file_gcDump Display
<GC: Growing heap from 200.0MPB to 532.0MB>
<GC: Growing heap from 532.0MB to 1024.0MB>

java.lang.OutOfMemoryError
at java.awt.image.DataBufferInt.<init>(DataBufferInt.java:41)
...
at Display$DisplayPanel.paintComponent(Display.java:20)
...

% tail -12 gdDump
<GC: Garbage Collection #9 with 1024.00 MB Heap (Compacting)>
<GC: Time since last collection : 0.02 seconds>
<GC: Time spent collecting : 0.43 seconds>
<GC: Data live before collection : 1023.80 MB>
<GC: Data live after collection : 1023.80 MB>
<GC: Data pinned during collection : 1020.47 MB>
<GC: Garbage Collection #10 with 1024.00 MB Heap (Compacting)>
<GC: Time since last collection : 0.00 seconds>
<GC: Time spent collecting : 0.44 seconds>
<GC: Data live before collection : 1023.80 MB>
<GC: Data live after collection : 1023.80 MB>
<GC: Data pinned during collection : 1020.48 MB>

% cat Display.java

import java.awt.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;

class Display extends JFrame
{
static BufferedImage bi = new BufferedImage (100, 200,
BufferedImage.TYPE_INT_ARGB);
static Rectangle r;

class DisplayPanel extends JPanel
{
public void paintComponent (Graphics g)
{
if (bi == null)
return;

r = getBounds ();
g.clearRect (0, 0, r.width, r.height);

g.drawImage (bi, 0, 0, r.width, r.height,
Color.black, null); // line 20

bi.flush ();
r = null;
}
}

Display ()
{
super ();

DisplayPanel dp = new DisplayPanel ();
getContentPane ().add (dp);
setSize (200, 300);
setVisible (true);

int rgb[] = new int[100*200];
int alpha = 0xff000000;
int shade = 50;
int inc = 1;

while (true)
{
int val = alpha + (shade << 16) + (shade << 8) + shade;
for (int i = 0; i < rgb.length; i++)
rgb = val;

shade += inc;
if (shade < 50 || shade > 250)
inc *= -1;

bi.setRGB (0, 0, 100, 200, rgb, 0, 100);

dp.repaint ();
}
}

public static void main (String args[])
{
new Display ();
}
}

The GC log file shows that memory continues to be allocated
but not reclaimed; indeed, the vast majority of it is pinned
for some mysterious reason. The first 8 entries show the
before & after values to be almost identical with the pinned
amounts very close nearby. Here are the values, the first
number being the before and second being the pinned:
#1 200MB heap: 187.22, 198.08
#2 532MB heap: 510.78, 528.81
#3 532MB heap: 530.77, 528.83
#4 1024MB heap: 992.42, 1020.17
#5 1024MB heap: 1023.77, 1020.44
#6 1024MB heap: 1023.75, 1020.42
#7 1024MB heap: 1023.80, 1020.41
#8 1024MB heap: 1023.77, 1020.39

I'm guessing that the two printed GC lines that appeared on
the screen occurred between #1 & #2, and #2 & #3.

Some of the things I've tried include adding an ImageObserver
class as the displayImage call's last arg, but every notification
received has the ALLBITS set with no other flags. I've tried
creating a Runnable class for the setRGB and repaint calls, and
then giving this Object to invokeLater and invokeAndWait. I've
tried getting the currentThread and forcing sleeps and yields,
and worsening the priority. I've tried putting a System.gc call
after each line in the above program, and running it with the
-Xmx<lots> option. But, all these tests lead to the same nasty
OOME.

It's a shame that a lot of the source files in java.awt.image
contain non-public vars, methods, constructors, etc; otherwise
it'd be possible (with a lot of work!) to extend Raster and
DataBufferInt, add some additional debug println's and try to
figure out what's going on. Depending on, I don't know, the
phase of the moon or the stock market, the program will display
something like 2300 or so images before tossing the OOME; but
it always occurs at line 41 of DataBufferInt.java which
declares a new int array of size width*height.

I can't get the above program to fail on my Windoze PC although
I've let it run for hours, and I'm starting to think that there
is an implementation problem with OSF1's JVM. Any help would be
very much appreciated,
 
R

Roedy Green

g.drawImage (bi, 0, 0, r.width, r.height,
Color.black, null); // line 20

bi.flush ();

This is a peculiar thing to do. How do you know the drawing is
complete and won't be needed again?
paint often gets called over and over with various slices.
 
G

Guest

As I said, I've tested this program with an ImageObserver and
it didn't seem to matter. Also, I've moved the flush to right
after the repaint call, but that didn't matter. In fact, I'm
not sure flush is doing anything; Sun's 1.3 source code that I
looked at has BufferedImage's flush as an empty method.

Or, are you suggesting that I not put the displayImage call
inside paintComponent at all? I haven't tried that; but how do
I grab a Graphics for the DisplayPanel in Display's constructor?

Thanks for your reply, but how would you suggest the Display
program be rearranged?
 
R

Roedy Green

(Roedy's reference link on Ratpacking was helpful, but just
slowed up receipt of the OOME by a little.)

Not ratpacking, packratting. A packrat is a rodent that collects stuff
for its nest, hence a person who has closets full of unused stuff is
called a "packrat"

Ratpacking sounds much more nefarious.
 
R

Roedy Green

I tried your code on JDK 1.5.0_04. It seems to work fine other than
jerkiness.

Here are a few comments.

1. normally for a JFrame you don't do setVisible in the constructor.
The caller of the constructor does the setVisible.

2. after you realise the component with setVisible, you must do all
your gui stuff on the edt thread. Therefore your repaint method
should be done with invokeLater. (Unless repaint is one of the thread
safe methods. I saw nothing to indicate it is.)

3. I think you should split the loop off from the constructor, even if
only to allow the caller to call setVisible as is traditional. The
loop has nothing to do with constructing.

4. You should not be doing a flush in paintComponent. Often
paintComponent gets called many times and it needs the image for
subsequent attempts. You flush it on the first one giving it nothing
to paint. Flush should be called just prior to creating a new image.
Try dragging the frame around,resizing it, occluding and revealing it
and dump the clip region in the paintComponent to see what I mean.


5. Your code reminds me of the relentless love making style of a porn
star. You just blindly keep changing the image trusting repaint to
keep up. There is no reason why it should. What you want is to detect
when the painting is complete before changing the image yet again.
Check the boolean result from drawImage. When it is true, you can
trigger another round. I suggest putting the main thread to sleep and
allowing it to be wakened prematurely if the painting completes. This
way the program will recover if for some reason painting never
completes.

6. There is no need to clear a rectangle if you completely paint it
right afterward. You also should be declaring your beast opaque so
Swing won't bother clearing either. Those unnecessary clearings cause
flicker.

7. For what you are doing, paintImmediately is also an option.
See http://mindprod.com/jgloss/paint.html
 
R

Roedy Green

As I said, I've tested this program with an ImageObserver and
it didn't seem to matter. Also, I've moved the flush to right
after the repaint call, but that didn't matter. In fact, I'm
not sure flush is doing anything; Sun's 1.3 source code that I
looked at has BufferedImage's flush as an empty method.

You are taking advantage of a timing peculiarity which will not
necessarily hold on all platforms or if you start buggering around
with the component -- dragging it around, burying it, etc.

It won't even hold on your machine if you get some other work going in
the background erratically soaking up your cpu time.
 
R

Roedy Green

Or, are you suggesting that I not put the displayImage call
inside paintComponent at all? I haven't tried that; but how do
I grab a Graphics for the DisplayPanel in Display's constructor?


The painting code always has to be inside paintComponent. In another
post I suggested two ways of keeping your paint and generate logic is
sync, not letting one get behind the other. One with a sleep and wake
and the other with paintImmediately.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,990
Messages
2,570,211
Members
46,796
Latest member
SteveBreed

Latest Threads

Top