Link to home
Start Free TrialLog in
Avatar of sciuriware
sciuriware

asked on

Memory leak by "fat" JDialogs?

Hello,
I got some applications that launch a dialog for reporting purposes.
In there I load some large pictures and/or create some large arrays.
I noticed that every time I do so my memory usage grows by some Mb's,
finally pushing me out.

I tried .dispose(); but that doesn't help.
Code:

Launched:

public class Xdialog extends JDialog
{
/// builds a heavy GUI,
/// has a button OK,
/// NO setVisible.

public void display()
{
     setVisible();  // Here the Launcher switches it on.
}

Launcher:

Xdialog x = new Xdialog(..........................);
     x.display;
     x.dispose();     // Does not release the memory used.

     x = null;           // Same ...

What did I do wrong?

;JOOP!

       JDialog xyz = new
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

Has anything else gained a reference to the dialog's members?
Avatar of sciuriware
sciuriware

ASKER

That's what I've been searching for for a long time but I just can't find it.

Was my code correct sofar?

;JOOP!
Well the skeleton you provided looks OK, but i think something *must* have gained a reference to it
As the code I listed is the only 'access' to the dialog object, the mysterious references
must be made inside the code in the dialog.

The only thing I can think of is to 'de-robe' the dialog, until I can present the code without
disturbing or hidden calls.
Give me some time.

;JOOP!
ASKER CERTIFIED SOLUTION
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
leaks more likely to be in the loading of large images.
Are you flushing the images once they are no longer needed?

>>> Are you flushing the images once they are no longer needed?

How?

As I had this problem in several applications where pictures were not used,
I made a weird solution: creating the dialog only once and replacing its contents.
Still I wonder what goes wrong.

;JOOP!
can you reproduce in a small example only involving dialogs?

That's what I'm going to  do, but my application is rather complex:
I must eliminate all 'smart' library calls that transform the pictures.

;JOOP!
Here you are (looks awful but ...):

Caller code:

               if(fileTarget.exists())
               {
                  Xdialog x;

                  x = new Xdialog
                  (
                     fr.f,
                     pathTarget.substring(ProLibSurvey.offsetInside),
                     fileTarget
                  );
                  x.setVisible(true);
                  x.dispose();
................................................

The dialog class:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

import TuLam.Im;

/**
 * @author     J.F.Lanting
 * @since      09-Aug-2008
 */

public class Xdialog extends JDialog implements ActionListener
{
   /**
    * Button that stops the dialog.
    */
   private JButton seenButton = null;

/*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*/
/*    CONSTRUCTOR SECTION:                                                   */
/*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*/

   /**
    * Constructor.
    *
    * @param outFile file for the picture outside ADLiB.
    * @param inPath short pathname of the picture inside ADLiB.
    * @param inFile file for the picture inside ADLiB.
    */
   public Xdialog
   (
      File outFile,
      String inPath,
      File inFile
   )
   {
      super((JFrame)null, true);
      super.setTitle("Afbeeldingen die verschillen met die in ADLiB");

      String outName = outFile.getAbsolutePath();
      String inName = inFile.getAbsolutePath();
      Container c;
      JPanel picturePane;
      JPanel p;
      Box pictureBox;
      Image picture;

// Build dialog:

      c = getContentPane();
      c.setLayout(new BorderLayout());
     
      picturePane = new JPanel(new FlowLayout());
     
// Picture left:
     
      pictureBox = Box.createVerticalBox();
     
      picture = bestFit(new ImageIcon(outName).getImage(), 400, 300);
      pictureBox.add(new JLabel(new ImageIcon(picture)));
     
      picturePane.add(pictureBox);
     
// Picture right:
     
      pictureBox = Box.createVerticalBox();
     
      picture = bestFit(new ImageIcon(inName).getImage(), 450, 400);
      pictureBox.add(new JLabel(new ImageIcon(picture)));
     
      picturePane.add(pictureBox);
     
      c.add(picturePane, BorderLayout.CENTER);

// Button:
     
      p = new JPanel(new FlowLayout());
     
      p.add(seenButton = new JButton("OK"));
      seenButton.addActionListener(this);
     
      c.add(p, BorderLayout.SOUTH);

// Finish:
     
      setSize(1000, 750);
      setLocationRelativeTo(null);
   }

/*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*/
/*    METHOD SECTION:                                                        */
/*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*/

   /**
    * Resizes an Image to a best fit in a rectangle keeping the aspect ratio.
    *
    * @param picture image to resize.
    * @param width maximum width for the result.
    * @param height maximum height for the result.
    * @return the resulting resized image.
    */
   Image bestFit(Image picture, int width, int height)
   {
      double x = picture.getWidth(null);
      double y = picture.getHeight(null);
      double xx = width / x;
      double yy = height / y;
      double factor = Math.min(xx, yy);
     
      return(Im.resize(picture, factor, factor));
   }

   /**
    * Resizes an Image by stretching it to a new size by x factor and y factor.
    *
    * @param picture the initial image.
    * @param xFactor the width stretching factor.
    * @param yFactor the height stretching factor.
    * @return the stretched image.
    */
   public static Image resize(Image picture, double xFactor, double yFactor)
   {
      BufferedImage buffer;
      Graphics2D g;
      AffineTransform transformer;
      AffineTransformOp operation;

      buffer = new BufferedImage
      (
         picture.getWidth(null),
         picture.getHeight(null),
         BufferedImage.TYPE_INT_ARGB
      );
      g = buffer.createGraphics();
      g.drawImage(picture, 0, 0, null);
      transformer = new AffineTransform();
      transformer.scale(xFactor, yFactor);
      operation = new AffineTransformOp(transformer, AffineTransformOp.TYPE_BILINEAR);
      buffer = operation.filter(buffer, null);
      return(Toolkit.getDefaultToolkit().createImage(buffer.getSource()));
   }
   
   /** (non-Javadoc)
    * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
    * @param a action event.
    */
   public void actionPerformed(ActionEvent a)
   {
      Object o = a.getSource();

      if(o == seenButton)
      {
         setVisible(false);
      }
   }
}

After initialization, Runtime.getRuntime.totalMemory() shows 13Mb,
after the first dialog with 2 pictures: 18Mb, then 20Mb, then 23Mb,
which is about proportional to the size of the pictures (*.jpg),
the fatal blow is a picture of 19Mb (71Mb as .bmp), which makes memory exceed 64Mb.
Running the application with a limit of 1000Mb reveals that memory grows to 277Mb.
When the same 12 pictures are repeated several times the memory remains at 277Mb
and is never returned.

;JOOP!
If you remove the image does the memory usage still grow.
From the description is sounds like its the image loading which would be expected.
Are the images likely to be needed more than once or only loaded once? ie. do u want them cached?

How do I remove the image(s)?

All images are to be presented once to a user to decide which ones are good: no caching.

;JOOP!
try calling image.flush() when its no longer needed.

Try loading the images with ImageIO, not using ImageIcon
I made 2 instance variables:

   Image picture1 = null;
   Image picture2 = null;

to replace 'picture' in the constructor and changed the handler:

      if(o == seenButton)
      {
         picture1.flush();
         picture2.flush();
         setVisible(false);
      }

............... doesn't help.

;JOOP!
I must be outdoors for a moment, then I will try ImageIO


;JOOP!
        picture1.flush();
         picture2.flush();

you also want to nulling the two vars


And remember that the gc may have not got around to freeing up memory yet :)

I tried everything.
null assignment to the Images also did not help.
null assignment to each and every variable did (of course) not help.
Memory usage keeps on growing.
I don't have the faintest idea where resources are still being referenced from.
It seems that somehow instances of that dialog remain in memory with the resources they have used.

The Image stuff is certainly NOT the cause.
I got 2 other applications where such a dialog brings trouble with only enormous quantities of text.

If nobody comes up with something better, then I have 2 options:

1) instantiate a steady JDialog of this type in the beginning and only replace the pictures
between displays.

2) extend my main JFrame and put those pictures in there to the cost of other components.

;JOOP!
I did some experiment to test case #2:

         String[] photos = {"370651.jpg", "370793.jpg", "370027.jpg", "370806.jpg", "370271.jpg"};
         JLabel p = new JLabel();  // Is put in our main frame somewhere.
       
         for(String s : photos)
         {
            File f = new File(dir, s);  // dir is the directory with all the pictures.

            Image i = new ImageIcon(f.getAbsolutePath()).getImage();  // Loads OK
           
            p.setIcon(new ImageIcon(i)); // Displays correctly
         }
         
:: all the pictures display OK, but the memory goes up to a certain amount and is never released.

The wasted heap remains the same, even if I run the program through several sequences
of the same photos.

I would like to reformulate and narrow this question to: where did my heap go in this tiny case?

;JOOP!
Did you try loading the images with ImageIO?
ImageIO produces BufferedImage.
How can I deploy ImageIO in the above code?

ImageIcon is self-monitored, is ImageIO?

Still remains the question: does a JAVA program keep all the pictures it once read?

;JOOP!
Well this:

picture = bestFit(new ImageIcon(outName).getImage(), 400, 300);

is less than optimal as normally:

Y y = new X().getY()

means you have created an instance of X that is uncollectable since you're referencing its Y

http://java.sun.com/javase/6/docs/api/javax/imageio/ImageIO.html#read(java.io.InputStream)
>>> means you have created an instance of X that is uncollectable since you're referencing its Y

can you explain this message, I don't get it.

;JOOP!
CEHJ, I browsed a bit through the EE database and I saw you practice
the same code to read an image

                Image q = new ImageIcon(outName).getImage();

So, what is wrong?

;JOOP!
ROTFL :-D

objects, after the amusement, what to do?

;JOOP!
>>and I saw you practice the same code to read an image

Yes - you're right - i've done that in the past. No doubt for the same reason that you're doing it: namely to avoid the PITA that comes with the default, asynchronous method of loading images. My excuse was that i did that before ImageIO came along, and i was too lazy to use MediaTracker.

Do you need help with ImageIO.read still?
Yes, please.

Otherwise I have a weird solution:
every time I want to display a pair of images (not very frequent though),
 I spawn a JAVA program that dies afterward: no memory problems.

;JOOP!
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hello,
first I must take some statements back: in this application CEHJ's ImageIO works!
But ..................
It seems that JAVA caches all Image data somewhere.
So I sit stuck with the rescaled pictures that I can't get rid of.

What I'm going to do now has to do with the nature of my application:
showing some pictures is only one task.
Afterward a lot of work must be done in other area's and I must get rid of those Mb's.
So, I choose for a spawn that shows the pictures and dies on a button click.
It's remarkable how fast this 'solution' is: almost as fast as the one-program-approach.

I thank you all.
;JOOP!
Last question: could I have used JAVA console to monitor this problem?

;JOOP!
> It seems that JAVA caches all Image data somewhere.

which is what I originally told you :)
You need to clear that cache once you don't need it anymore

> Last question: could I have used JAVA console to monitor this problem?

thought you were :)
just add some logging of the used memory
Sorry - i can't look at this until later
>>> You need to clear that cache once you don't need it anymore

No idea how to access it.  flush() did not do it at least.

;JOOP!
always worked fine for me, u sure you're not maintaining refs somewhere?
did u run a profiler on it yet?

I don't have a profiler; never (seemed to) need it until now.
And from isolated tests I know there are no other references.
I gave up, I know, by retreating to my 'solution'.

;JOOP!
If you're still interested in the causes, you'd probably be better off using an IDE to do the profiling. E.g.

http://www.netbeans.org/kb/60/java/profiler-intro.html
I'm addicted to ECLIPSE.

;JOOP!
Thanks a lot.
I'm going to close this thread; learned something.

;JOOP!
Thanks for all of your time.
;JOOP!
:-)

Let me know what you find if you profile
Promised. But finishing my program has priority (.......  as it always had ...........).
.... retired and still chasing clouds ...................

;JOOP!