DePhille
DePhille

Reputation: 105

JavaFX OutOfMemoryError with animated GIFs

I was creating a JavaFX 2.0 application featuring an image browser which should be capable of displaying animated GIFs when I encountered some OutOfMemoryError exceptions after browsing though several GIFs. I managed to isolate the relevant code into a "GifCrasher" application:

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;

import javafx.scene.image.Image;


public class GifCrash {

    // Settings:
    private static  long            waitTime    =   100;    // msec
    private static  ArrayList<File> imageFiles  =   new ArrayList<File>() {{
                                                    add(new File("thrillercat.gif"));
                                                }};

    // Other:
    private static  long    totalSize   =   0;
    private static  long    gifsLoaded  =   0;

    public static void main(String[] args) throws Exception {

        while(!Thread.currentThread().isInterrupted()) {
            // Read gif file:
            File        imageFile   =   GifCrash.imageFiles.get((int) (GifCrash.gifsLoaded % GifCrash.imageFiles.size()));
            InputStream iStream     =   new FileInputStream(imageFile);
            Image       image       =   new Image(iStream);
            iStream.close();

            // Display info:
            GifCrash.gifsLoaded++;
            GifCrash.totalSize += imageFile.length();
            System.out.println("Loaded " + imageFile + " (" + imageFile.length() + " bytes)");
            System.out.println("GifCount\t=\t" + GifCrash.gifsLoaded);
            System.out.println("TotalSize\t=\t" + Math.round((double) GifCrash.totalSize / (1024 * 1024)) + " MBytes (" + GifCrash.totalSize + " bytes)");
            System.out.println();

            // Wait?
            if (GifCrash.waitTime > 0) {
                Thread.sleep(GifCrash.waitTime);
            }
        }
    }
}

This simple application builds javafx Image objects without actually doing anything with them and thus, as far as I know, these objects should be garbage collected. In the example application I simulate loading multiple different GIFs by reloading the same GIF each time instead of caching it somewhere (so that I don't have to find >250MBytes of GIF files). I also added an optional waitTime parameter to make sure the garbage collector has an opportunity to free up some memory. However, running this application with an animated GIF file in imageFiles will still produce an OutOfMemoryError after a while (in my case after loading approximately 250MBytes worth of animated GIFs). Running the application with a PNG file is no problem at all, it seems as if animated GIFs are the only problem.

These are the images I used for testing: thrillercat.gif and catdestroyer.png. This is the (truncated) output I got when running the application with thrillercat.gif:

Loaded thrillercat.gif (1203120 bytes)
GifCount    =   1
TotalSize   =   1 MBytes (1203120 bytes)

Loaded thrillercat.gif (1203120 bytes)
GifCount    =   2
TotalSize   =   2 MBytes (2406240 bytes)

...

Loaded thrillercat.gif (1203120 bytes)
GifCount    =   225
TotalSize   =   258 MBytes (270702000 bytes)

Loaded thrillercat.gif (1203120 bytes)
GifCount    =   226
TotalSize   =   259 MBytes (271905120 bytes)

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at com.sun.javafx.iio.gif.GIFImageLoader2.decodePalette(GIFImageLoader2.java:288)
    at com.sun.javafx.iio.gif.GIFImageLoader2.load(GIFImageLoader2.java:191)
    at com.sun.javafx.iio.ImageStorage.loadAll(ImageStorage.java:294)
    at com.sun.javafx.iio.ImageStorage.loadAll(ImageStorage.java:244)
    at com.sun.javafx.tk.quantum.PrismImageLoader2.loadAll(PrismImageLoader2.java:107)
    at com.sun.javafx.tk.quantum.PrismImageLoader2.<init>(PrismImageLoader2.java:41)
    at com.sun.javafx.tk.quantum.QuantumToolkit.loadImage(QuantumToolkit.java:607)
    at javafx.scene.image.Image.loadImage(Image.java:942)
    at javafx.scene.image.Image.initialize(Image.java:722)
    at javafx.scene.image.Image.<init>(Image.java:625)
    at GifCrash.main(GifCrash.java:27)

As always I assume this is due to some error in my code rather than a bug, so what am I doing wrong here?

In case this is a bug, is there a way to work around it? i.e. I need to be able to display a large amount of animated GIFs in a JavaFX window (with only one GIF being visible at a time).

Thanks!

Upvotes: 4

Views: 2076

Answers (1)

jewelsea
jewelsea

Reputation: 159486

Just use Java 8, your sample program works with the Java 8 version.

I tried the sample program on Java 7u45 (OS X 10.8) (MacBook Air 2012, 4gb ram) and it consistently ran out of memory after 71 iterations.

Whereas with Java 8, the program never ran out of memory after running for 5000 iterations:

Loaded /Users/lilyshard/dev/playfx/src/thriller-cat-o.gif (1203120 bytes)
GifCount    =   10000
TotalSize   =   11474 MBytes (12031200000 bytes)

I have no workaround for you to make your program work on Java 7u45 and it is unlikely that bug fixes made on the Java 8 branch will be back-ported to JavaFX 2.2. Sometimes bug fixes are back-ported on user request through the JavaFX issue tracker, but it is a rare occurrence.

My guess is that are some bugs in the JavaFX 2.2 gif loader which have been fixed in preparation for the Java 8 release.

Upvotes: 5

Related Questions