Подтвердить что ты не робот

Низкая производительность графического интерфейса Java 8 по сравнению с Java 6

В Java 6 ниже код работает как ожидалось, но на Java 8 он занимает гораздо больше времени. Интересная часть состоит в том, что компоненты используют тот же метод setEnable() для включения и выключения компонентов, но отключение вызова занимает гораздо больше времени, чем разрешение, что почти в два раза больше. Отключение в Java 8 занимает гораздо больше времени, чем в Java 1.6. Вопрос в том, почему это происходит? Является ли это проблемой производительности Java 8?

Вот результаты для 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();
            }
        });
    }
}
4b9b3361

Ответ 1

Согласно моему профилировщику, операция большую часть времени проводит в методе Thread.holdsLock, что может быть действительно дорогостоящей операцией, которая вызывается Component.checkTreeLock, которая косвенно называется Component.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.currentTimeMillis() и System.nanoTime(), поскольку последний является правильным инструментом для измерения прошедшего времени.

Ответ 2

У меня не было возможности точно понять, но похоже, что обработка событий могла измениться в 8. Отключение родителя ускоряет процесс:

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