Reputation: 105
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
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