简体   繁体   English

JFrame永远不会被垃圾收集

[英]JFrame is never garbage collected

I have an application that needs to open multiple JFrames (it's a log viewer, and sometimes you need to see a bunch of logs in separate windows to compare). 我有一个需要打开多个JFrame的应用程序(这是一个日志查看器,有时您需要在单独的窗口中查看一堆日志进行比较)。

It appears that the JVM (Java 8 update 101 on OS X) is holding a strong reference to the JFrame, which is preventing it from being garbage collected, and eventually leads to an OutOfMemoryError being thrown. 看来JVM(OS X上的Java 8更新101)对JFrame拥有很强的引用,这防止了JFrame被垃圾回收,并最终导致抛出OutOfMemoryError。

To see the problem, run this problem with a max heap size of 200 megabytes. 若要解决此问题,请以200兆字节的最大堆大小运行此问题。 Each time a window is opened, it consumes 50 megabytes of RAM. 每次打开一个窗口时,它都会消耗50 MB的RAM。 Open three windows (using 150 megabytes of RAM). 打开三个窗口(使用150 MB的RAM)。 Then close the three windows (which calls dispose), which should free up memory. 然后关闭三个窗口(称为Dispose),这将释放内存。 Then try to open a fourth window. 然后尝试打开第四个窗口。 An OutOfMemoryError is thrown and the fourth window does not open. 引发OutOfMemoryError并且第四个窗口没有打开。

I've seen other answers stating that memory will be automatically released when necessary to avoid running out, but that doesn't seem to be happening. 我已经看到其他答案,指出在必要时会自动释放内存以避免耗尽内存,但这似乎没有发生。

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() );
    }
}

As shown here , there is an irreducible leak due to unrecoverable allocations related to a typical host peer component. 如所示在这里 ,存在由于与一个典型的主机对等组件不可恢复的分配一个不可约泄漏。 The remnant is ~2 MB in the course of creating and disposing ~10 3 windows. 在创建和配置〜10 3个窗口的过程中,剩余空间为〜2 MB。 In your case, the dominant leak is due to retained instances of bigMemoryChunk . 在您的情况下,主要的泄漏是由于bigMemoryChunk保留实例。 One approach is to make the instances unreachable in a WindowListener . 一种方法是使实例在WindowListener 不可访问

this.addWindowListener(new WindowAdapter() {

    @Override
    public void windowClosing(WindowEvent e) {
        bigMemoryChunk = null;
    }
});

Why do we need to set bigMemoryChunk = null ? 为什么我们需要设置bigMemoryChunk = null

JFrame has no direct way to know that that each instance in your program has an associated instance of bigMemoryChunk . JFrame无法直接知道程序中的每个实例都有一个bigMemoryChunk关联实例。 Such an object becomes eligible for garbage collection when it is unrechable ; 当此类对象不可回收时,它就有资格进行垃圾回收; bigMemoryChunk is the only reference to the array object in this case, so setting it to null makes it immediately eligible for later garbage collection. 在这种情况下, bigMemoryChunk是对数组对象的唯一引用,因此将其设置为null使其立即有资格进行后续垃圾回收。

If the JFrame is the only thing holding a reference to bigMemoryChunk …then why don't the JFrame and bigMemoryChunk …both get garbage collected after the window has been disposed? 如果JFrame是唯一持有对bigMemoryChunk的引用的……那么,为什么JFramebigMemoryChunk ……在处理完窗口之后都没有收集到垃圾呢?

You may be confusing containment with inheritance and composition . 您可能会产生混淆遏制继承组合 The JFrame isn't "holding a reference to bigMemoryChunk ;" JFrame不是“持有对bigMemoryChunk的引用”; the JFrame has an instance variable named bigMemoryChunk that holds a reference to an array object. JFrame 有一个名为bigMemoryChunk 实例变量,其中包含对数组对象的引用。 The small amount of memory lost to the frame's peer is owned and managed by the host. 丢失给帧对等方的少量内存由主机拥有和管理。 The large amount of memory in bigMemoryChunk is your program's responsibility. bigMemoryChunk的大量内存是程序的责任。 The enclosed WindowListener allows you to associate management of the array object with closing the frame. 随附的WindowListener允许您将数组对象的管理与关闭框架相关联。

The profile below shows a series of four subframes opened; 下面的配置文件显示了打开的一系列四个子帧; each one is then closed, followed by a forced garbage collection in the profiler. 然后关闭每个窗口,然后在事件探查器中强制进行垃圾回收。

轮廓

As profiled: 简介:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.NumberFormat;

public class WindowLeakTest {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame launcherWindow = new JFrame("Launcher window");
                launcherWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
                JButton launcherButton = new JButton("Open new JFrame");
                launcherButton.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        JFrame subFrame = new JFrame("Sub frame") {
                            private byte[] bigMemoryChunk = new byte[50 * 1024 * 1024];

                            {
                                this.addWindowListener(new WindowAdapter() {

                                    @Override
                                    public void windowClosing(WindowEvent e) {
                                        bigMemoryChunk = null;
                                    }
                                });
                            }

                            @Override
                            protected void finalize() throws Throwable {
                                super.finalize();
                                System.out.println("Finalizing window.");
                            }
                        };
                        subFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
                        subFrame.add(new JLabel("Nothing to see here"));
                        subFrame.pack();
                        subFrame.setVisible(true);
                    }
                });
                launcherWindow.add(launcherButton);
                launcherWindow.pack();
                launcherWindow.setVisible(true);
            }
        });
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM