与 Java 6 相比,Java 8 的 GUI 性能较差

在 Java 6 中,下面的代码按预期运行,但在 Java 8 中,它需要花费更多的时间。有趣的是,组件使用相同的方法来启用和禁用组件,但禁用调用比启用调用花费的时间要长得多,几乎是启用调用的两倍。在 Java 8 中禁用比在 Java 1.6 中禁用所需的时间要长得多。问题是为什么会发生这种情况?这是 Java 8 的性能问题吗?setEnable()

以下是 Java 6 的结果:

    Sun Microsystems Inc. 1.6.0_45
    Initializing GUI
    GUI initialized in 1105 ms
    Disabling
    Disabled in 687 ms
    Enabling
    Enabled in 375 ms

以下是 Java 8 的结果:

    Oracle Corporation 1.8.0_25
    Initializing GUI
    GUI initialized in 604 ms
    Disabling
    Disabled in 6341 ms
    Enabling
    Enabled in 370 ms

代码:

import java.awt.Component;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

public class TestGUI extends JFrame implements ActionListener {

    private static final long serialVersionUID = 1L;

    public TestGUI() {
        initGUI();
    }

    public void actionPerformed(ActionEvent e) {
        String text;
        if(e.getActionCommand().equals("Enable-ALL")){
            enableAll();
            text= "Disable-ALL";
        }
        else{
            disableAll();
            text= "Enable-ALL";
        }
        ((JButton)e.getSource()).setText(text);
        ((JButton)e.getSource()).setEnabled(true);

    }


    private  void initGUI() {
        long m = System.currentTimeMillis();
        System.out.println("Initializing GUI");
        setTitle(System.getProperty("java.vendor") + " " + System.getProperty("java.version"));
        setLayout(new FlowLayout());

        JButton b = new JButton("Disable-ALL ");
        b.addActionListener(this);
        add(b);

        for (int i = 1; i < 10001; i++) {
            b = new JButton("Button " + i);
            add(b);
        }
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(600, 600);
        setVisible(true);
        m = System.currentTimeMillis() - m;
        System.out.println("GUI initialized in " + m + " ms");
    }

    private void disableAll() {
        long m = System.currentTimeMillis();
        System.out.println("Disabling");
        for (Component c : getContentPane().getComponents()) {
            c.setEnabled(false);
        }

        m = System.currentTimeMillis() - m;
        System.out.println("Disabled in " + m + " ms");
    }

    private void enableAll() {
        long m = System.currentTimeMillis();
        System.out.println("Enabling");
        for (Component c : getContentPane().getComponents()) {
            c.setEnabled(true);
            invalidate();
        }
        m = System.currentTimeMillis() - m;
        System.out.println("Enabled in " + m + " ms");
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            public void run() {
                System.out.println(System.getProperty("java.vendor") + " "
                        + System.getProperty("java.version"));
                new TestGUI();
            }
        });
    }
}

答案 1

根据我的分析器,该操作将大部分时间花在方法上,这确实是一个昂贵的操作,由哪个调用由 间接调用。Thread.holdsLockComponent.checkTreeLockComponent.updateCursorImmediately

通常,在更新多个组件时,您可以通过在操作之前和之后调用来避免昂贵的视觉更新,例如getContentPane().setVisible(false);getContentPane().setVisible(true);

private void disableAll() {
    long m = System.currentTimeMillis();
    System.out.println("Disabling");
    getContentPane().setVisible(false);
    for (Component c : getContentPane().getComponents()) {
        c.setEnabled(false);
    }
    getContentPane().setVisible(true);

    m = System.currentTimeMillis() - m;
    System.out.println("Disabled in " + m + " ms");
}

您将看到,无论哪种视觉更新导致问题的细节,此类问题都将消失。

因此,您无需考虑如何在此处正确进行基准测试,并不是说操作需要几秒钟的时间并不重要,但我建议了解与System.nanoTime()之间的区别,因为后者是测量经过时间的正确工具。System.currentTimeMillis()


答案 2

我还没有机会确切地理解,但似乎事件处理可能在8中发生了变化。首先禁用父级可加快该过程:

getContentPane().setEnabled(false);
for (Component c : getContentPane().getComponents()) {
    c.setEnabled(false);
}
getContentPane().setEnabled(true);

推荐