Why does the JFrame object seem to stay alive, even though there aren't references to it?

Aviv Cohn :

If I remember correctly, in Java one can write

new JFrame();

without saving the JFrame reference in any variable. This program will display a GUI window on screen and leave it open, until the program is closed. (Please correct me if this premise is false).

Theoretically, we could think that since the JFrame is now unreachable from user code, the GC should at some point free the object. And as a result, the associated OS resources (the GUI window, etc.) will be freed as well.

However, if my understanding is correct - that program is a valid and working Java program (albeit a pretty useless one), and it will not crash or behave in strange ways at any point (again correct me if this premise is wrong).

It seems that the GC will not collect the JFrame object even though there aren't any references to it, or at least it will not cause the associated OS resources to be freed.

I would like to understand: How is this possible?

  1. Does the constructor of JFrame save this anywhere in order to not be collected? In the source code for JFrame's superclass java.awt.Frame (method noteFrame) the frame seems to be saved in a weak references queue. But since these are weak references, that doesn't seem to explain things

  2. Is it in fact the case that the GC does eventually free the JFrame - however, the finalizer method of JFrame doesn't free the associated OS resources, thus the GUI window stays open?

  3. Any other explanation?

Please note that this is a follow up question to a more theoretical question I posted on SE.SE. This question is different because it relates specifically to the Java implementation details.

Holger :

A JFrame that is only created via new JFrame() without any additional actions will not get opened on the screen and not prevented from garbage collection. Only a frame connected to a display device will be referenced from the AWT implementation and can’t get garbage collected before it has been explicitly disconnected.

Which can be verified with the following code:

public static void main(String[] args) {
    check(new JFrame(), "just creating a JFrame", x -> {});
    check(new JFrame(), "creating and connecting a JFrame", JFrame::pack);
    check(Frame.getFrames()[0], "calling dispose()", Frame::dispose);
private static <T> void check(T obj, String description, Consumer<T> action) {
    WeakReference<T> r = new WeakReference<>(obj);
    obj = null;
    if(r.get() == null) System.out.println("collected immediately");
    else {
        if(r.get() == null) System.out.println("collected after finalization");
        else System.out.println("still alive");

which will print

just creating a JFrame
collected immediately
creating and connecting a JFrame
still alive
calling dispose()
collected after finalization

in typical implementations.

It’s worth noting that a frame that was never connected to a display device gets collected immediately like an ordinary object, whereas a frame which was connected and later-on disconnected via a dispose() call requires a cleanup action, which takes place after the first garbage collector run.

Further, the example uses pack() rather than setVisible(true), to demonstrate that a frame can get connected to a display device without being visible. This makes the difference between isDisplayable() which reflects whether the component is connected to a screen device and isVisible() which tells whether the visible property has been set to true. Finally, a component does only report isShowing() as true when it is visible and displayable and all of its parents are showing.

Guess you like