0

I have created a program that loads image with FileDialog, resize it, previews it to users, and after button click saves it to a folder.

My problem is:

  1. when I run my program - RAM usage ~50mb
  2. loading 1mb JPG file - RAM usage ~93mb
  3. saving 1mb JPG file - RAM usage ~160mb

I intend this program to be lightweight, but after 3-4 files it occupies 500mb RAM space.

I tried to use System.gc(); every time user saves file, but it reduced RAM usage only by ~10%.

Below is a code with loading and saving images, full code, you can find HERE.

BTW - why when after loading 1mb JPG and then saving it size increase to 10mb?

Code for loading image:

    FileDialog imageFinder = new FileDialog((Frame)null, "Select file to open:");
            imageFinder.setFile("*.jpg; *.png; *.gif; *.jpeg");
            imageFinder.setMode(FileDialog.LOAD);
            imageFinder.setVisible(true);
            userImagePath = new File(imageFinder.getDirectory()).getAbsolutePath()+"\\"+imageFinder.getFile();

            userImagePath = userImagePath.replace("\\", "/");

Code for saving image:

 BufferedImage bimage = new BufferedImage(userImage.getWidth(null), userImage.getHeight(null), BufferedImage.TYPE_INT_ARGB);

                    Graphics2D bGr = bimage.createGraphics();
                    bGr.drawImage(userImage, 0, 0, null);
                    bGr.dispose();

                    try {
                        BufferedImage bi = bimage;
                        File outputfile = new File("C:\\Users\\Mariola\\git\\MySQL-viwer\\MySQL viewer\\src\\database_images\\"+userBreedInfo[0]+".jpg");
                        ImageIO.write(bi, "png", outputfile);
                    } catch (IOException e1) {

                    }
            }
            System.gc()
  • 2
    I don't recommend an empty catch block. At least log the exception. – AndiCover Aug 08 '19 at 10:38
  • 3
    You should remember that jpg is a compressed image format. When it is uncompressed into a BufferedImage each pixel may be represented by 4 integer values (RGBA) depending on your image format/color space. This means that a 100x100 pixel image takes up 40,000 integer values. – mwarren Aug 08 '19 at 10:39
  • `ImageIO.write` is likely to be the culprit here. This is not necessarily a leak, the JVM may just not release resources. You can forget about `System.gc()`, this acts merely as a suggestion, is not reliable and widely advised against using. – Koenigsberg Aug 08 '19 at 10:43
  • @Mär - is there a better way to save the image and release resources afterwards? – Łukasz Karasiński Aug 08 '19 at 10:46
  • Not sure. You can see if you can limit the overall RAM used by your application, but I think that is managed by the JVM, so on a customer's machine you may not be able to set this. Other than that there are suggestions to pass a `FileStream` to `ImageIO` and make sure to close it afterwards. All in all you are pretty much at the mercy of the respective JVM, sadly: https://stackoverflow.com/questions/1567979/how-to-free-memory-in-java – Koenigsberg Aug 08 '19 at 10:48
  • 1
    *"I intend this program to be lightweight, but after 3-4 files it occupies 500mb RAM space."* Then launch the JVM with limited RAM! The JVM will only GC when it sees a need to do so. See [What is the XY problem?](http://meta.stackexchange.com/q/66377) – Andrew Thompson Aug 08 '19 at 11:32
  • First file I viewed in your github "View.java" creats a `Connection`, `Statement` and `ResultSet` and leaves them open. This is unrelated to the current question but is definitely your next resource leak. – Sebastian Aug 08 '19 at 11:34

1 Answers1

1

The "problem" is that ImageIO kind of uses much memory. Then this memory will not be returned to the OS (that's why even a no-need call to System.gc() will not return it) because that's how JVM works.(Java 13 promises that memory will be returned to the OS?) As @Andrew Thompson pointed out in the comment section, if you want less memory consumption take a look at this question. If you run it you will see that with memory limit it will not consume so much. Which actually tells you not to worry about it. JVM will do its magic and will handle memory consumption according to how much memory the OS says its free.

If it still bothers you, you could try to find any ImageIO alternatives that may behave differently. In my opinion though, it does not worth it for your needs. I mean, you just want to save/load an image.

Another worth-to-read question is Why is it bad practice to call System.gc()?

George Z.
  • 6,024
  • 4
  • 18
  • 33
  • Thanks! I will do as you said. I set my VM arguments to _-Xmx200m_ and as expected program runs under 260mb. I will try to find some better solution than `ImageIO`. – Łukasz Karasiński Aug 08 '19 at 14:32
  • I just hoped that maybe there is some way to limit RAM in the program, not as JVM starting arguments :) – Łukasz Karasiński Aug 08 '19 at 14:40
  • 2
    It's not so much that `ImageIO` uses much memory, but rather that keeping decoded pixels in memory (like a `BufferedImage` does) *requires* a certain amount of memory (width * height * bytesPerPixel at least). – Harald K Aug 08 '19 at 15:53