Issue
I'm currently working on a 2D top-down game written in Java(FX). With each new game world a random map (background and vegetation) will be created based on Perlin Noise, the amount of created objects lies between 5000 - 7000. The individual parts of the vegetation (trees, bushes, rocks, etc.) are represented as rectangles "filled" with an image.
While playing the game, a player can exit a current world and create a new random one whenever he/she wants. When doing so, 5000 - 7000 new objects will be created that all need an image.
As far as I know there are two ways in JavaFX to add an image to a rectangle:
- By using rectangle.setFill(new ImagePattern(new Image(path)))
- Or by adding a CSS styleclass and using -fx-fill: url(path)
With the first option I very soon ran into OutOfMemoryErrors, while the second option runs perfectly smooth. I decided to start up VisualVM to see how big the difference in memory usage actually is, and it is insane. See the screenshots from VisualVM below to get an idea (3 new world are being created):
While both options store the image data within the memory's byte[], I'm wondering where this huge difference in memory usage is comming from? Is CSS somehow able to "fill" the rectangle without creating some sort of an image container and the image itself? If so, wouldn't it be possible to adapt that to JavaFXs setFill() method? What am I missing?😅
Solution
You appear to be creating a new Image
for every rectangle. If you're creating 5000-7000 rectangles, you then have 5000-7000 Image
instances, which presumably replicate a lot of the same image data.
Instead, just cache the Image
s so you only load one per path:
private final Map<String, Image> imageCache = new HashMap<>();
and then just
// rectangle.setFill(new ImagePattern(new Image(path));
rectangle.setFill(new ImagePattern(imageCache.computeIfAbsent(path, Image::new)));
If your "worlds" use different images which are not shared between them, you can call imageCache.clear()
before loading a new world. Depending on your use case, it might be beneficial to cache the ImagePattern
instead of the Image
, though I am guessing that ImagePattern
is a pretty lightweight wrapper around the image it uses.
Answered By - James_D
Answer Checked By - Pedro (JavaFixing Volunteer)