import javax.swing.*;
public class Main
{
public Main()
{
JFrame jf = new JFrame("Demo");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(100, 100);
jf.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new Main();
}
});
Runtime.getRuntime().gc();
}
}
I call Runtime.getRuntime().gc(); for explicit garbage collector invoking. But window doesn't dissapear from screen, why doesn't garbage collector reclaim JFrame's object?
When a JFrame is created, it registers itself in some internal Swing data structures which allow it to receive events like mouse clicks. This means there is a reference to your object lurking somewhere until you tell Swing to get rid of the window using dispose().
Given the invokeLater() call, the call to GC will probably occur 1st1.
BTW - calling Runtime.gc() is generally pointless, the JRE won't GC till it needs to.
E.G.
Output
GC called
Frame visible
Code
package test;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class VisibleFrameGC {
VisibleFrameGC() {
JFrame jf = new JFrame("Demo");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(100, 100);
jf.setVisible(true);
System.out.println("Frame visible");
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new VisibleFrameGC();
}
});
Runtime.getRuntime().gc();
System.out.println("GC called");
}
}
The frame is visible and the reference to the object is reachable by at least one of the GUI threads (the Event Dispatch Thread). That is why it isn't garbage collected.
If you want it to disapear, use frame.dispose().
Related
I've spent the past 2 days trying to debug a memory leak in my application and have now narrowed it down to one JFrame that is not being properly disposed.
It is started from a Control Frame via the following snippet:
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new LeakingInterface();
}
});
The Frame itself has setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); set.
It contains a Button that saves the changes made within the Frame and then disposes as well as the typical close button. The "save changes"-button is worded as follows:
saveChanges.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
saveChanges();
dispose();
}
});
Now to my problem: The Frame is not properly unloaded from memory. I debugged this with the Eclipse Memory Analyzer. When closing the Frame via the OS-provided close button none of the Frames are unloaded/deleted, the "probable leaks"-screen says the following after and closing the frame 11 times:
11 instances of "LeakingInterface", loaded by "sun.misc.Launcher$AppClassLoader # 0x6c00f08b8" occupy 393.71 MB (97,10%) bytes.
These instances are referenced from one instance of "java.lang.Thread", loaded by "<system class loader>"
Weirdly enough closing screens via the save changes button unloads some of the JFrames and the "probable leaks"-screen gives me different answers. Again for 11 Frames loaded and closed:
One instance of "LeakingInterface" loaded by "sun.misc.Launcher$AppClassLoader # 0x6c01bc1f8" occupies 36.21 MB (19,20%) bytes. The memory is accumulated in one instance of "LeakingInterface" loaded by "sun.misc.Launcher$AppClassLoader # 0x6c01bc1f8".
This message shows up 5 times, but only 5 times and the occupied memory is much smaller than closing by OS exit button.
I'm at the end of my wits now what causes this and how I can fix it. The debugging was done on Mac OS but the same behaviour (Memory Leak) can be seen on Windows as well.
Things I have tried that produced no result:
1) Converting "LeakingInterface" into a singleton class like this:
public static LeakingInterface showCardChangeInterface(){
if(instance != null){
instance.dispose();
instance = null;
System.gc(); //Trying to make sure the gc runs at least once
}
instance = new LeakingInterface();
return instance;
}
2) Instead of using just a constructor to show the Frame instead convert the frame into a field and making sure it is properly nulled and disposed before re-opening:
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(leakingFrame != null){
leakingFrame.dispose();
}
leakingFrame = new LeakingFrame();
}
});
3) Trying to replicate at least some of the frames properly disposing I tried to overload the OS Button to mirror the behaviour of the saveChanges button.
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent event) {
saveChanges();
dispose();
}
});
I'm very confused how this can provide different results.
E: As suggested here is a minimal example that can reproduce this behavior:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
public class LeakingFrame extends JFrame{
public static LeakingFrame instance;
private ArrayList<String> content;
public static void main(String[] args) {
JFrame testFrame = new JFrame("test");
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton startButton = new JButton("Open LeakingFrame");
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
LeakingFrame.showInstance();
}
});
testFrame.add(startButton);
testFrame.pack();
testFrame.setVisible(true);
}
public static LeakingFrame showInstance(){
if(instance!=null){
instance.dispose();
instance = null;
System.gc();
}
instance = new LeakingFrame();
return instance;
}
private LeakingFrame(){
super("LeakingFrame");
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent event) {
dispose();
}
});
content = new ArrayList<String>();
for(int i=0;i<150000;i++){
content.add("LARGESTRING"); //So it takes up at least some amount of memory
}
JButton dispose = new JButton("Dispose");
dispose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
add(dispose);
pack();
setVisible(true);
}
}
testing a game, i sometimes get exceptions thrown when a component is not displayable. i added a wait loop on isDisplayable().
seems like my game can take a few hundred ms. to become displayable.
is this a sane way to handle this problem?
i am testing game clients that talk over sockets to a server.
thanks
edit 1: thanks for the comments. i discovered that i am adding the mediator for the gui (an observer) to the model (an observable) before the gui completes its construction and initialization. it gets worse, as i am initializing a panel with calls to createImage which returns null and throws.
import java.awt.*;
import javax.swing.*;
public class WaitForFrameToBeVisible {
JFrame frame=new JFrame("FrameDemo");
private void createAndShowGUI() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel emptyLabel=new JLabel("");
emptyLabel.setPreferredSize(new Dimension(175,100));
frame.getContentPane().add(emptyLabel,BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
void run() throws Exception {
System.out.println("waiting for frame to be displayable");
long t0=System.nanoTime();
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
while (!frame.isDisplayable())
Thread.sleep(1);
long dt=System.nanoTime()-t0;
System.out.println("waited "+dt/1_000_000.+" ms. for frame to be displayable");
}
public static void main(String[] args) throws Exception {
new WaitForFrameToBeVisible().run();
}
}
You don't need to use isDisplayable(). You could use invokeAndWait(...) for what you are trying to do :
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createAndShowGUI();
}
});
// control will reach here when the GUI will be created
Notice that this way you are not actually checking if the frame is visible but you are checking if createAndShowGUI() has done its work
Another thing is you should not call isDisplayable() or any function like this in loop. It will consume unnecessary processing speed. Instead use wait-notify.
But as far as your case is concerned to use WindowListener is very good idea as suggested by MadProgrammer in comment.
What is the correct way of disposing a frame which is created inside a Runnable object?
The code below returns a null pointer exception when the endDialog is called before the LoadingRunnable has completed its constructor.
How can the endDialog be executed after the constructor has finished?
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LoadingRunnable implements Runnable
{
private JFrame jFrame;
#Override
public void run()
{
jFrame = new JFrame("Window");
JPanel jPanel = new JPanel();
JLabel label = new JLabel("Loading...");
jPanel.add(label);
jFrame.setContentPane(jPanel);
jFrame.pack();
jFrame.setVisible(true);
}
public void endDialog()
{
jFrame.setVisible(false);
jFrame.dispose();
}
public static void main(String args[])
{
LoadingRunnable l = new LoadingRunnable();
SwingUtilities.invokeLater(l);
//work done here
l.endDialog();
}
};
You have a concurrency problem here because SwingUtilities.invokeLater() schedules your runnable class execution in the Event Dispatch Thread asynchronously while your main thread's flow still running, causing a NPE.
The correct way to dispose a frame is through events, just as Swing is designed to be used. For instance by clicking the "X" (close) button or by dispatching a WindowEvent:
frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
You may want to take a look to this question too: Optional way to close a dialog window
In addition
If you just want to show something during your application start up, then you can use SplashScreen API instead of JFrame. See How to Create a Splash Screen for further details.
Based on your previous question and this new one, I'd suggest you read the whole Concurrency in Swing tutorial to understand about common concurrency problems in Swing and how to deal with them.
Ok found how:
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Loading
{
private JFrame jFrame;
public void startDialog()
{
jFrame = new JFrame("Window");
JPanel jPanel = new JPanel();
JLabel label = new JLabel("Loading...");
jPanel.add(label);
jFrame.setContentPane(jPanel);
jFrame.pack();
jFrame.setVisible(true);
}
public void endDialog()
{
jFrame.setVisible(false);
jFrame.dispose();
}
public static void main(String args[])
{
final Loading l = new Loading();
for (int i = 0; i < 200; i++)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
l.startDialog();
}
});
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
l.endDialog();
}
});
}
}
};
Is there a way how to use a dialog in Swing which prohibits any gui activity under it but at the same time DOES NOT stop execution on the thread where it was set to visible?
Yes it can be done .
dlg.setModal(false);
or
dlg.setModalityType(Dialog.ModalityType.MODELESS);
where dlg is instance of your JDialog .
The basic idea of a JDialog IS to block the underlying thread until the user reacts to it. If you need to run something on the UI thread which should not be interrupted, consider using an additional worker thread for it. This way, the UI will be blocked by the JDialog, but the underlying process won't.
Yes, there is a little trick to make it work. We simply deactivate modality and manually disable the JFrame we want to make unclickable.
private final static JDialog dialog; static {
JOptionPane pane = new JOptionPane();
pane.setOptions(new Object[]{}); // Removes all buttons
dialog = pane.createDialog(frame, ""); // Create dialog with pane
dialog.setModal(false); // IMPORTANT! Now the thread isn't blocked
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
}
Now you can use it like this:
dialog.setVisible(true);
frame.setEnabled(false);
// Logic
dialog.setVisible(false);
frame.setEnabled(true);
Technically, no. Like MadProgrammer wrote in a comment, you are never expected to access any Swing component off-EDT, JDialogs included, therefore the situation you hinted at in the question can never happen (there can never be any thread other than EDT that sets a dialog visible).
You could make it seem like it is, though. That's what SwingUtilities.invokeLater(Runnable) is for (doc).
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class BlockingDialogDemo extends JFrame {
private Timer timer;
private JDialog blocker;
public BlockingDialogDemo() {
setTitle("Blocking Dialog");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 200);
setLocationRelativeTo(null);
blocker = new JDialog(this, true);
blocker.setLayout(new FlowLayout());
blocker.setUndecorated(true);
blocker.getRootPane().setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.black));
blocker.add(new JLabel("I'm blocking EDT!"));
JProgressBar progress = new JProgressBar();
progress.setIndeterminate(true);
blocker.add(progress);
blocker.pack();
timer = new Timer(3000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
doSomeWork();
}
});
timer.setRepeats(false);
timer.start();
}
private void doSomeWork() {
// this executes on-EDT
Runnable runnable = new Runnable() {
public void run() {
// this executes off-EDT - never ever access Swing components here
showBlocker();
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
System.out.println("Ummm.. I was sleeping here!");
} finally {
hideBlocker();
}
}
};
new Thread(runnable).start();
}
private void showBlocker() {
// this executes off-EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// this executes on-EDT
blocker.setLocationRelativeTo(BlockingDialogDemo.this);
blocker.setVisible(true);
}
});
}
private void hideBlocker() {
// this executes off-EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// this executes on-EDT
blocker.setVisible(false);
timer.restart();
}
});
}
public static void main(String[] args) {
// this is called off-EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// this is called on-EDT
new BlockingDialogDemo().setVisible(true);
}
});
}
}
This works for me... sometimes:
public class NonBlockingModalDialogDemo extends JFrame{
JButton btnDoIt;
public NonBlockingModalDialogDemo() {
setTitle("NonBlockingModalDialog Demo");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300,300);
setLayout(new FlowLayout());
btnDoIt = new JButton("Non-Blocking Notify");
btnDoIt.addActionListener( new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
JDialog asyncDialog = createNonBlockingModalDialog("Please wait while we do some work", "Please wait");
doWork(50);
//Once your done, just dispose the dialog to allow access to GUI
asyncDialog.dispose();
}
});
this.add(btnDoIt);
}
private JDialog createNonBlockingModalDialog(String message, String title)
{
final JDialog dialog = new JDialog();
dialog.setLayout(new FlowLayout());
dialog.add(new JLabel(message));
dialog.setTitle(title);
dialog.setModal(true);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setAlwaysOnTop(true);
dialog.pack();
Runnable dialogDisplayThread = new Runnable() {
public void run() {
dialog.setVisible(true);
}};
new Thread(dialogDisplayThread).start();
//Need to wait until dialog is fully visible and then paint it
//or else it doesn't show up right
while(!dialog.isVisible()){/*Busy wait*/}
dialog.paint(dialog.getGraphics());
return dialog;
}
private void doWork(int amount) {
for(int i = 0; i < amount; i++)
{
System.out.println("Doing work step number " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
System.out.println("done");
}
public static void main(String[] args) {
new NonBlockingModalDialogDemo().setVisible(true);
}
}
I don't really like that it has a busy wait in it to check if the Dialog is visible yet, but so far I haven't found a way around it. At any rate, the busy wait should not take very long at all, so it really shouldn't matter.
Edit:
I did something very similar to this and for some reason, on some machines, sometimes, it just blocks forever without even showing the dialog.
I haven't figured out the root cause, but this leads me to conclude that all the people who say "never modify the GUI outside of the Event Dispatch Thread" may be on to something.
Perhaps rather than trying to continue the work you need to do on the EDT, maybe you should try something like this:
https://stackoverflow.com/a/4413563/2423283
Which uses SwingWorker to spawn a new thread and then allows you to update the GUI components when you are done.
Is there a way to send the java frame in front of every other opened program. I know you can use
JFrame.setAlwaysOnTop(true);
but that just keeps it in front allways. I want it to only happen when a certain function is called. For instance, When I press a button on a frame, it will wait using Thread.sleep(10000) for ten seconds, but the I want it to just the frame to the front in case you clicked out of the window for a second. Any suggestions?
Take a look at Window#toFront
You may also want to take a look at
WindowListener
Swing Timer
Be careful of using Thread.sleep in a GUI environment, if used incorrectly, this will cause you window to stop updating (painting)
This is surprisingly fiddly.
The exact behavior might also depend on the operating system. But at least on Windows, a call to frame.toFront() will not necessarily bring the window to the front. Instead, it will cause the corresponding entry in the task bar to blink for a few seconds. I tried something like
f.setAlwaysOnTop(true);
f.setAlwaysOnTop(false);
which basically works, but after the window was brought to the front, it is not "active", and none of my attempts to make it active worked (e.g. requesting the focus or so).
The only solution that I found now to (reliably) work (on Windows, at least) was
if (!f.isActive())
{
f.setState(JFrame.ICONIFIED);
f.setState(JFrame.NORMAL);
}
But wonder wheter there is a more elegant solution.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class FrameToTopTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
final JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Bring me to top after 3 seconds");
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
triggerBringToFront(f, 3000);
}
});
f.getContentPane().add(button);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static void triggerBringToFront(final JFrame f, final int delayMS)
{
Timer timer = new Timer(delayMS, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
// This will only cause the task bar entry
// for this frame to blink
//f.toFront();
// This will bring the window to the front,
// but not make it the "active" one
//f.setAlwaysOnTop(true);
//f.setAlwaysOnTop(false);
if (!f.isActive())
{
f.setState(JFrame.ICONIFIED);
f.setState(JFrame.NORMAL);
}
}
});
timer.setRepeats(false);
timer.start();
}
}