为什么 JFrame 对象似乎保持活动状态,即使没有对它的引用?
Why does the JFrame object seem to stay alive, even though there aren't references to it?
如果我没记错的话,在Java里面可以写
new JFrame();
而不保存任何变量中的 JFrame
引用。该程序将在屏幕上显示一个 GUI window 并保持打开状态,直到程序关闭。 (如果这个前提是错误的,请纠正我)。
理论上,我们可以认为,由于 JFrame
现在无法从用户代码访问,GC 应该在某个时候释放该对象。因此,关联的 OS 资源(GUI window 等)也将被释放。
但是,如果我的理解是正确的 - 该程序是一个有效且有效的 Java 程序(尽管是一个非常无用的程序),并且它不会在任何时候崩溃或以奇怪的方式运行(再次正确如果这个前提是错误的我。
似乎 GC 不会收集 JFrame
对象,即使没有对它的任何引用,或者至少它不会导致相关联的 OS 资源被释放。
我想明白了:这怎么可能?
JFrame
的构造函数是否将this
保存在任何地方,以免被收集?在 source code for JFrame
's superclass java.awt.Frame(方法 noteFrame
)中,框架似乎保存在弱引用队列中。但由于这些是弱引用,这似乎并不能解释事情
事实上,GC 确实 最终释放了 JFrame
- 但是,JFrame
的终结器方法不会 释放关联的 OS 资源,因此 GUI window 保持打开状态?
还有其他解释吗?
请注意,这是对 theoretical question I posted on SE.SE 的后续问题。这个问题是不同的,因为它具体涉及 Java 实施细节。
仅通过 new JFrame()
创建而没有任何其他操作的 JFrame
将不会在屏幕上打开,也不会阻止垃圾回收。只有连接到显示设备的框架才会从 AWT 实现中引用,并且在显式断开连接之前无法进行垃圾回收。
可通过以下代码验证:
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) {
System.out.println(description);
action.accept(obj);
WeakReference<T> r = new WeakReference<>(obj);
obj = null;
System.gc();
if(r.get() == null) System.out.println("collected immediately");
else {
System.runFinalization();
System.gc();
if(r.get() == null) System.out.println("collected after finalization");
else System.out.println("still alive");
}
}
这将打印
just creating a JFrame
collected immediately
creating and connecting a JFrame
still alive
calling dispose()
collected after finalization
在典型的实现中。
值得注意的是,从未连接到显示设备的帧会像普通对象一样立即被收集,而通过 dispose()
调用连接但后来断开连接的帧需要清理操作,发生在第一个垃圾收集器 运行.
之后
此外,该示例使用 pack()
而不是 setVisible(true)
,以演示框架可以在不可见的情况下连接到显示设备。这使得 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()
与 true
之间存在差异,当它可见且可显示并且其所有父级都在显示时。
如果我没记错的话,在Java里面可以写
new JFrame();
而不保存任何变量中的 JFrame
引用。该程序将在屏幕上显示一个 GUI window 并保持打开状态,直到程序关闭。 (如果这个前提是错误的,请纠正我)。
理论上,我们可以认为,由于 JFrame
现在无法从用户代码访问,GC 应该在某个时候释放该对象。因此,关联的 OS 资源(GUI window 等)也将被释放。
但是,如果我的理解是正确的 - 该程序是一个有效且有效的 Java 程序(尽管是一个非常无用的程序),并且它不会在任何时候崩溃或以奇怪的方式运行(再次正确如果这个前提是错误的我。
似乎 GC 不会收集 JFrame
对象,即使没有对它的任何引用,或者至少它不会导致相关联的 OS 资源被释放。
我想明白了:这怎么可能?
JFrame
的构造函数是否将this
保存在任何地方,以免被收集?在 source code forJFrame
's superclass java.awt.Frame(方法noteFrame
)中,框架似乎保存在弱引用队列中。但由于这些是弱引用,这似乎并不能解释事情事实上,GC 确实 最终释放了
JFrame
- 但是,JFrame
的终结器方法不会 释放关联的 OS 资源,因此 GUI window 保持打开状态?还有其他解释吗?
请注意,这是对 theoretical question I posted on SE.SE 的后续问题。这个问题是不同的,因为它具体涉及 Java 实施细节。
仅通过 new JFrame()
创建而没有任何其他操作的 JFrame
将不会在屏幕上打开,也不会阻止垃圾回收。只有连接到显示设备的框架才会从 AWT 实现中引用,并且在显式断开连接之前无法进行垃圾回收。
可通过以下代码验证:
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) {
System.out.println(description);
action.accept(obj);
WeakReference<T> r = new WeakReference<>(obj);
obj = null;
System.gc();
if(r.get() == null) System.out.println("collected immediately");
else {
System.runFinalization();
System.gc();
if(r.get() == null) System.out.println("collected after finalization");
else System.out.println("still alive");
}
}
这将打印
just creating a JFrame
collected immediately
creating and connecting a JFrame
still alive
calling dispose()
collected after finalization
在典型的实现中。
值得注意的是,从未连接到显示设备的帧会像普通对象一样立即被收集,而通过 dispose()
调用连接但后来断开连接的帧需要清理操作,发生在第一个垃圾收集器 运行.
此外,该示例使用 pack()
而不是 setVisible(true)
,以演示框架可以在不可见的情况下连接到显示设备。这使得 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()
与 true
之间存在差异,当它可见且可显示并且其所有父级都在显示时。