【发布时间】:2017-01-19 03:12:41
【问题描述】:
我有一个需要打开多个 JFrame 的应用程序(它是一个日志查看器,有时您需要在单独的窗口中查看一堆日志进行比较)。
似乎 JVM(OS X 上的 Java 8 更新 101)持有对 JFrame 的强引用,这会阻止它被垃圾收集,并最终导致抛出 OutOfMemoryError。
要查看问题,请在最大堆大小为 200 兆字节的情况下运行此问题。每次打开一个窗口,都会消耗 50 兆字节的 RAM。打开三个窗口(使用 150 兆字节的 RAM)。然后关闭三个窗口(调用 dispose),这应该释放内存。然后尝试打开第四个窗口。抛出 OutOfMemoryError 并且第四个窗口没有打开。
我已经看到其他答案指出内存将在必要时自动释放以避免耗尽,但这似乎没有发生。
package com.prosc.swing;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
public class WindowLeakTest {
public static void main(String[] args) {
EventQueue.invokeLater( new Runnable() {
public void run() {
JFrame launcherWindow = new JFrame( "Launcher window" );
JButton launcherButton = new JButton( "Open new JFrame" );
launcherButton.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e ) {
JFrame subFrame = new JFrame( "Sub frame" ) {
private byte[] bigMemoryChunk = new byte[ 50 * 1024 * 1024 ]; //50 megabytes of memory
protected void finalize() throws Throwable {
System.out.println("Finalizing window (Never called until after OutOfMemory is thrown)");
super.finalize();
}
};
subFrame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
subFrame.add( new JLabel( "Nothing to see here" ) );
subFrame.pack();
subFrame.setVisible( true );
System.out.println( "Memory usage after new window: " + getMemoryInfo() );
}
} );
launcherWindow.add( launcherButton );
launcherWindow.pack();
launcherWindow.setVisible( true );
new Timer( 5000, new ActionListener() {
public void actionPerformed( ActionEvent e ) {
System.gc();
System.out.println( "Current memory usage after garbage collection: " + getMemoryInfo() );
}
} ).start();
}
} );
}
public static String getMemoryInfo() {
NumberFormat numberFormat = NumberFormat.getNumberInstance();
return "Max heap size is " + numberFormat.format( Runtime.getRuntime().maxMemory() ) + "; free memory is " + numberFormat.format( Runtime.getRuntime().freeMemory() ) + "; total memory is " + numberFormat.format( Runtime.getRuntime().totalMemory() );
}
}
【问题讨论】:
-
Javadoc Java SE 7 Window.dispose:“通过随后调用打包或显示来重建本机资源,可以使窗口及其子组件再次可显示。”这可能是引用仍然存在的原因。
-
可能重复:stackoverflow.com/questions/7376993/…。你同意吗?
-
它们几乎是同一个问题——不过我没有看到令人满意的答案。我需要多个窗口,这似乎是一个错误,开发人员负责在调用 dispose() 后清除窗口的内容。我希望代码示例可能会得到更好的答案。
-
Remove Top-Level Container on Runtime 的可能重复项; profile你的实际代码来查找问题点。
-
@trashgod:我在发布问题之前使用了分析器,发现一些根级本机对象持有对我的 JFrames 的引用。我试图找出为什么会发生这种情况以及这是否是我可以控制的。调用 dispose() 显然是不够的。
标签: java swing java-memory-leaks