Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Out Of Memory Exception #726

Closed
Frank-99 opened this issue Sep 2, 2023 · 8 comments
Closed

Out Of Memory Exception #726

Frank-99 opened this issue Sep 2, 2023 · 8 comments
Milestone

Comments

@Frank-99
Copy link

Frank-99 commented Sep 2, 2023

I feel like I'm going crazy....
Running the code below will result in an out of memory crash of the program! I have no idea why, even after a lot of tinkering around it... It always ends up filling around 1GB of RAM every 30 seconds or so.

I tried using the JSVG library without the FlatSVGIcon and it doesn't look like it's causing the problem.
Also, if I don't use the progressbar in indeterminate mode it's fine, and same if I have the indetermined progressbar but a normal JPanel instead of my custom one with the SVG background...
I so do not understand what is causing this

The Main Class:

package debug;

import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class Debug implements Runnable {

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Debug());
	}

	@Override
	public void run() {
		FlatLightLaf.setup();

		JFrame window = new JFrame();
		window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		window.setResizable(false);
		window.setPreferredSize(new Dimension(480, 300));

		JPanelBackground contentPanel = new JPanelBackground();
		contentPanel.setBackgroundImage(new FlatSVGIcon("debug/splash-bg-deco.svg").getImage());

		JProgressBar progress = new JProgressBar();
		progress.setIndeterminate(true);
		contentPanel.add(progress);

		window.add(contentPanel);
		window.pack();
		window.setVisible(true);
	}

}

The custom JPanel Class:

package debug;

import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;

public class JPanelBackground extends JPanel {

	private Image bgImage = null;

	public JPanelBackground() {
		super();
	}

	public JPanelBackground(Image backgroundImage) {
		super();
		bgImage = backgroundImage;
	}

	public void setBackgroundImage(Image backgroundImage) {
		bgImage = backgroundImage;
	}

	public Image getBackgroundImage() {
		return bgImage;
	}

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);

		if (bgImage != null) {
			g.drawImage(bgImage, 0, getHeight() - bgImage.getHeight(null), bgImage.getWidth(null), bgImage.getHeight(null), null);
		}
	}

}

The SVG file:

<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 255" width="480" height="255">
	<path fill="#E3E8F2" d="m480 0v255h-480v-61.2c92.5-14.1 165-29.7 205-39.2 78-20.1 133-44.2 178-67.3 59-33.1 82-60.2 97-87.3z"/>
	<path fill="#C7D0E2" d="m480 83v172h-480v-51.8c100-8.5 192.5-21.1 249-31.3 72-12.4 104.1-19.9 148.5-36 57.6-22.3 71.7-41.7 82.5-52.9z"/>
</svg>

I'm using JDK 17 with NetBeans IDE 18. The libraries are jsvg-1.0.0, flatlaf-extras-3.2 and flatlaf-3.1.1.

If needed I made a project just with the data necessary to cause this issue and I can share it

@DevCharly
Copy link
Collaborator

Tried your code on Windows 11, Java 17, and it works without consuming much memory. Only ~80 MB.

What operating system do you use?

@Frank-99
Copy link
Author

Frank-99 commented Sep 3, 2023

Windows 11 Pro 22H2 22621.2134
Java 17.0.8

I also tried on another PC with fresh install And I've also tried to build the project in a jar file but it results in the same error.
this is a link to a WeTransfer page with the zip file of the entire project and built jar if it helps...

Also, if I comment out the FlatLaf setup function it's also fine, it's just the combination of all three things that is the problem

@DevCharly
Copy link
Collaborator

Can not reproduce here...

What happens if you do not invoke g.drawImage()?

You could use https://visualvm.github.io/ to check what objects require that much memory.

When using indeterminate progressbar, it is repainted often. Since FlatLaf progressbar is not opaque (for rounded style), also the parent of the progressbar needs to repainted. Metal L&F has opaque square progressbar and does not repaint parent.

You could make the progressbar opaque to avoid repainting image:

progress.setOpaque( true );

@Frank-99
Copy link
Author

Frank-99 commented Sep 3, 2023

This is what the VisualVM profiled:
image
image
image

Also, setting the progressbar as opaque or commenting out the g.drawImage() doesn't cause the issue

The part that I don't understand is that it only happens If I use all three "features" together: the flatlaf theme, the progressbar set to indeterminate and my custom JPanel drawing the FlatSVGIcon Image...

@Frank-99
Copy link
Author

Frank-99 commented Sep 3, 2023

New tests:

If I replace contentPanel.setBackgroundImage(new FlatSVGIcon("debug/splash-bg-deco.svg").getImage()); with this:

	URL url = Debug.class.getResource("splash-bg-deco.svg");
	SVGDocument svgDocument = new SVGLoader().load(url);
	FloatSize size = svgDocument.size();
	BufferedImage image = new BufferedImage((int) size.width,(int) size.height, BufferedImage.TYPE_INT_ARGB);
	Graphics2D g = image.createGraphics();
	svgDocument.render(null,g);
	g.dispose();
	
	ontentPanel.setBackgroundImage(new FlatSVGIcon("debug/splash-bg-deco.svg").getImage());

then it works fine even though the image comes out very jaggedy.

If instead of my custom JPanel I do contentPanel.add(new JLabel(new FlatSVGIcon("debug/splash-bg-deco.svg"))); then It draws both the SVG and the progressbar at the same time no problem.

The more I try to understand it and the more I look at it and the more I get confused!

EDIT:

If I clone the image from the FlatSVGImage before passing it to my custom function then the resulting image comes out less sharp (might be the 1.25 scaling on my screen) but it doesn't cause the memory to overflow. see below:

BufferedImage copyOfImage = new BufferedImage(480, 255, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = copyOfImage.createGraphics();
g.drawImage(new FlatSVGIcon("debug/splash-bg-deco.svg").getImage(), 0, 0, null);
JPanelBackground contentPanel = new JPanelBackground();
contentPanel.setBackgroundImage(copyOfImage);

@DevCharly
Copy link
Collaborator

Thanks for all the information. That brought me to the right direction 👍
Found the reason 😄

There is a bug in MultiResolutionImageSupport.ProducerMultiResolutionImage, which uses IdentityHashMap for cache:

private final IdentityHashMap<Dimension, Image> cache = new IdentityHashMap<>();

(I've probably copied this from MultiResolutionImageSupport.MappedMultiResolutionImage, where it makes sense to use IdentityHashMap)

The cache key is of type Dimension. A new Dimension instance is created to access the cache. So the cache key does never match and the BufferedImage for the requested resolution is recreated (and added to cache) each time the image is drawn, which is done every 15ms in your case...

Working on a fix...

BTW initially was not able to reproduce the memory leak because Java 8 version of MultiResolutionImageSupport is used in my FlatLaf development environment, which does not have this problem.

@Frank-99
Copy link
Author

Frank-99 commented Sep 3, 2023

Perfect thank you for the explanation! I thought a bit more about it in the meanwhile and I did came to a similar ballpark conclusion, but this makes so much sense now ahahah
Looking forward to the fix, will just patch it momentarily to use a cloned image untill it's out!

Thank you very much for all the quick help!

DevCharly added a commit that referenced this issue Sep 4, 2023
@DevCharly
Copy link
Collaborator

Fixed in FlatLaf 3.2.1

@DevCharly DevCharly added this to the 3.2.1 milestone Sep 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants