This discussion is archived
6 Replies Latest reply: Jun 27, 2010 12:12 PM by 843802 RSS

How do I release memory when done with a large Image?

843802 Newbie
Currently Being Moderated
I've got a sample program here. Enter a filename of a .jpg file, click the button and it will load and display a thumbnail of it. However memory is not released so repeatedly clicking the button will let you watch the memory use grow and grow. What should be done in the code to release the large original image once the thumbnail is obtained? Here's the class:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.util.Iterator;
import javax.imageio.*;
import javax.imageio.stream.*;
import javax.swing.*;


public class ImageMemoryLeak extends JFrame implements ActionListener {
     private JLabel thumbnail = null;
     private JTextField tf = null;
     private JButton button = null;
     
     public static void main(String[] args) {
          ImageMemoryLeak leaker = new ImageMemoryLeak();
          try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); }
          catch (Exception ex) { }
          leaker.showDialog();
     }
     private void showDialog() {
          setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          setBounds(100,100,200,200);
          setBackground(new Color(255,255,255));
          Container cont = getContentPane();
          cont.setBackground(Color.lightGray);
          cont.setLayout(new FlowLayout());
          thumbnail = new JLabel("thumbnail here");      
          cont.add(thumbnail);
          tf = new JTextField("type filename here");     
          cont.add(tf);
          button = new JButton("Load Image");
          button.addActionListener(this);
          cont.add(button);
          pack();
          setVisible(true);
     }
     
     public void actionPerformed(ActionEvent e) {
          if (e.getSource() == button) {
               String fname = tf.getText();
               File f = new File(fname);
               if (f.exists()) {                    
                    try {
                         // This is where a file is loaded and thumbnail created
                         Iterator<ImageReader> iter = ImageIO.getImageReadersByFormatName("jpeg");
                         ImageReader imgrdr = iter.next();
                         ImageInputStream iis = ImageIO.createImageInputStream(f);     
                         imgrdr.setInput(iis, true);
                         ImageReadParam param = imgrdr.getDefaultReadParam();
                         BufferedImage fullSizeImage = imgrdr.read(0, param);
                         imgrdr.dispose();     // is this enough?     
                         iis.close();               
                         
                         int thWidth = 150;
                         int thHeight = 150;
                         int w = fullSizeImage.getWidth(null);
                         int h = fullSizeImage.getHeight(null);
                         double ratio = (double)w/(double)h;
                         if (w>h) thHeight = (int)((double)thHeight /ratio);
                         else if (w<h) thWidth = (int)((double)thWidth * ratio);
                         
                         BufferedImage thumbImage = new BufferedImage(thWidth, thHeight, BufferedImage.TYPE_INT_RGB);
                         Graphics2D graphics2D = thumbImage.createGraphics();
                         graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                         RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                         graphics2D.drawImage(fullSizeImage, 0, 0, thWidth, thHeight, null);
                         // done with fullSizeImage now - how to release it though?
                         fullSizeImage.flush();     // doesn't seem to do the trick
                         ImageIcon oldicon = (ImageIcon)thumbnail.getIcon();
                         if (oldicon != null) oldicon.getImage().flush();
                         ImageIcon icon = new ImageIcon(thumbImage);
                         thumbnail.setIcon(icon);
                         thumbnail.setText("");
                         pack();

                    } catch (IOException e1) {
                         e1.printStackTrace();
                    }
               }
               else {
                    thumbnail.setText("file not found");
               }
          }
     }
}
  • 1. Re: How do I release memory when done with a large Image?
    843802 Newbie
    Currently Being Moderated
    I don't think you can do that on Java, you'll have to remove it's references and wait for GC truck to collect it.
  • 2. Re: How do I release memory when done with a large Image?
    843802 Newbie
    Currently Being Moderated
    LightPepsi wrote:
    I don't think you can do that on Java, you'll have to remove it's references and wait for GC truck to collect it.
    I don't mind having GC getting to it but it's not doing so now. So my question is how should the above program change so that references are removed and it will eventually GC properly?
  • 3. Re: How do I release memory when done with a large Image?
    793415 Pro
    Currently Being Moderated
    lecisco wrote:
    LightPepsi wrote:
    I don't think you can do that on Java, you'll have to remove it's references and wait for GC truck to collect it.
    I don't mind having GC getting to it but it's not doing so now. ..
    Isn't it? GC is only called when the JRE feels it is necessary. Definitive evidence of a memory leak is generally revealed by an OutOfMemoryError. Does the code throw OOMEs?

    That question brings me to the code sample. Typing an image file name in a text field is soooo 1980s. It would take a long time and much hard work on the part of the person testing it, in order to get to an OOME.

    It would be better to change the example so it:
    1) Uses a JFileChooser to specify a directory in which to search for images.
    2) Iterates all JPEGs in the directory.

    I can't speak for others, but I have directories containing hundreds of 5 MegaPixel images. If an OOME is going to manifest, those type of image directories should do the trick.
  • 4. Re: How do I release memory when done with a large Image?
    843802 Newbie
    Currently Being Moderated
    AndrewThompson64 wrote:
    lecisco wrote:
    I don't mind having GC getting to it but it's not doing so now. ..
    Isn't it? GC is only called when the JRE feels it is necessary. Definitive evidence of a memory leak is generally revealed by an OutOfMemoryError. Does the code throw OOMEs?
    Excellent point. I tried loading over and over to force an OOME. I found that after the memory footprint grew to about 750mb, it reset down to about 130mb again, so it seems that GC does eventually kick in. Perhaps what I have is fine.
    That question brings me to the code sample. Typing an image file name in a text field is soooo 1980s. It would take a long time and much hard work on the part of the person testing it, in order to get to an OOME.
    In my actual application I'm using drag-and-drop to specify the image. The text field was just to simplify the code sample. How you get the file location is not relevant for the question - it's how the resources are released. If you're saying my code is fine regarding and shouldn't be holding onto any resources, that's great. I am looking to confirm I'm following best practices regarding image resources.

    With the code sample, you just have to specify one .jpg file and repeatedly click the button to see the memory grow, so there's no burden to type a new file each time.
  • 5. Re: How do I release memory when done with a large Image?
    793415 Pro
    Currently Being Moderated
    lecisco wrote:
    AndrewThompson64 wrote:
    lecisco wrote:
    I don't mind having GC getting to it but it's not doing so now. ..
    Isn't it? GC is only called when the JRE feels it is necessary. Definitive evidence of a memory leak is generally revealed by an OutOfMemoryError. Does the code throw OOMEs?
    Excellent point. I tried loading over and over to force an OOME. I found that after the memory footprint grew to about 750mb, it reset down to about 130mb again, so it seems that GC does eventually kick in. Perhaps what I have is fine.
    That question brings me to the code sample. Typing an image file name in a text field is soooo 1980s. It would take a long time and much hard work on the part of the person testing it, in order to get to an OOME.
    In my actual application I'm using drag-and-drop to specify the image. The text field was just to simplify the code sample. How you get the file location is not relevant for the question - it's how the resources are released. If you're saying my code is fine regarding and shouldn't be holding onto any resources, that's great.
    I'm not saying that. So far I've not looked closely at the code, and am no expert on resource caching in any case.
    ..I am looking to confirm I'm following best practices regarding image resources.
    Good show. There is too much rubbish code out there.
    ..With the code sample, you just have to specify one .jpg file and repeatedly click the button to see the memory grow, so there's no burden to type a new file each time.
    Oh right, my bad. I had presumed it required a different image each time. Still (grumbles) a file chooser to select the image file would not have gone astray - for us lazy types. ;)
  • 6. Re: How do I release memory when done with a large Image?
    843802 Newbie
    Currently Being Moderated
    I'm marking this as answered because it seems to be working - just needs to hit a certain heap size before gc kicks in. Thanks for the assistance.