2016-11-05 4 views
2

Я пытаюсь создать диаграмму эскизов некоторых данных с использованием JScrollPane, но я сталкиваюсь с трудностями с производительностью. Этот пример содержит около 100 миниатюрных диаграмм с 5000 образцами в каждом из них. Когда я пытаюсь несколько раз прокручиваться и возвращаться в несколько раз, прокрутка происходит с задержкой, увеличением загрузки процессора, использованием памяти приложения до 500 Мб.Каков правильный способ создания эскизов?

Есть ли способ избежать этой проблемы с производительностью без сокращения моих данных?

enter image description here

import java.awt.Color; 
import java.awt.EventQueue; 
import java.awt.GridLayout; 
import java.util.Random; 

import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 

import org.jfree.chart.ChartFactory; 
import org.jfree.chart.ChartPanel; 
import org.jfree.chart.JFreeChart; 
import org.jfree.chart.plot.PlotOrientation; 
import org.jfree.chart.plot.ThermometerPlot; 
import org.jfree.data.general.DefaultValueDataset; 
import org.jfree.data.xy.XYSeries; 
import org.jfree.data.xy.XYSeriesCollection; 

public class ThumbnailChartsTest extends JPanel { 
private static final int W = 200; 
private static final int H = W; 
private static final int N = 5000; 
private static final Random random = new Random(); 

private static ChartPanel createPane() { 
    final XYSeries series = new XYSeries("Data"); 
    for (int i = 0; i < random.nextInt(N) + N; i++) { 
     series.add(i, random.nextGaussian()); 
    } 
    XYSeriesCollection dataset = new XYSeriesCollection(series); 

    JFreeChart chart = ChartFactory.createXYLineChart("Random", "Domain", 
     "Range", dataset, PlotOrientation.VERTICAL, false, false, false); 
    return new ChartPanel(chart, W, H, W, H, W, H, 
      false, true, true, true, true, true); 
} 

public static void main(final String[] args) { 

    EventQueue.invokeLater(new Runnable() { 

     @Override 
     public void run() { 
      JFrame f = new JFrame("Test"); 
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
      JPanel panel = new JPanel(); 
      panel.setLayout(new GridLayout(0, 4)); 
      for (int i=0; i<100; i++){ 
       panel.add(createPane()); 
      } 

      JScrollPane scrollPane = new JScrollPane(panel, 
        JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 
        JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 
      f.add(scrollPane); 

      f.pack(); 
      f.setVisible(true); 
     } 
    }); 

} 
} 

Edit: Я не могу понять одну вещь: почему в этом случае использование памяти еще очень огромный! Посмотрите на эту иллюстрацию.

image

Дополнение Я думаю, что есть какое-то недоразумение.

Размер кучи на мониторе visualVM enter image description here После запуска размер кучи апплета составляет всего 125 МБ, это круто. Но потом я начинаю тестирование: прокрутка и изменение размера несколько раз, все больше и больше - вверх и вниз, вверх и вниз, меньшая рамка и больший кадр. Куча размером более 500 Мб! Я полагаю, что эта ситуация не является нормальной.

Дополнение # 2

Реальный пример:

Мои данные имеют размер около всего 2 Мб и представлена ​​в 90 графиках (2 серии в каждом), одна серия содержит 3000 элементов. Я реализовал изменение столбцов чисел с помощью ползунка. enter image description here

Но для этого небольшого размера кучи данных растут более 1,5 ГБ!

enter image description here

Это происходит после того, как некоторые действия, изменяя число столбцов, например, Для моего процессора (ядро 2 дуэта 2,2 ГГц) каждая таблица чертежей занимает около 4 секунд! С этой большой задержкой трудно управлять слайдером.

обновление:

Я внедрено субдискретизации мои данные 100 выборок на диаграмме миниатюр. Теперь это определенно быстрее, но проблема с огромным размером кучи все еще там. На картинке выше 700 Мб, и это не рекорд. Я разочарован.

ответ

4

Используйте flyweight pattern для отображения только видимых графиков. Подход, используемый JTablerenderers, обозначен here и показан на рисунке ChartRenderer. Для иллюстрации, набор данных воссоздается каждый раз, когда ячейка обнаруживается; прокручивать, изменять размер и переключать приложения, чтобы увидеть эффект. Хотя такой рендеринг хорошо масштабируется в десятки тысяч ячеек, каждый график по-прежнему отображает N точек данных. Вы можете ограничить количество видимых ячеек в реализации метода Scrollable, getPreferredScrollableViewportSize(), как показано ниже.

Как уменьшить использование памяти до небольших значений?

Там нет общего ответа, но несколько стратегий могут оказаться полезными:

  • Compose графика, как в начале инициализации программы, как это возможно, а не в то время они были оказаны; приведенный ниже пример updated создает TableModel из ChartPanel экземпляров; ChartRenderer соответственно проще.

  • Карты, имеющие более чем несколько тысяч точек, фактически не читаются; рассмотрите усечение больших наборов данных и отображение полных данных только в ответ на ListSelectionEvent, проиллюстрированный here.

  • Мониторы активности платформ могут вводить в заблуждение; profile, чтобы проверить фактические результаты.

image

После запуска апплета это не большое, менее чем 200 Мб, но после того, как прокрутка, изменение размера и т.д. использования памяти достигает значения более чем 600 Мб. Зачем?

Типичный profiler вид кода ниже показывает только умеренное использование и ожидаемое free/used ratio после прокрутки и garbage collection; ваши результаты могут отличаться.

profile

import java.awt.Component; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.util.Random; 
import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.table.DefaultTableModel; 
import javax.swing.table.TableCellRenderer; 
import org.jfree.chart.ChartFactory; 
import org.jfree.chart.ChartPanel; 
import org.jfree.chart.JFreeChart; 
import org.jfree.data.xy.XYSeries; 
import org.jfree.data.xy.XYSeriesCollection; 

/** 
* @see https://stackoverflow.com/a/40445144/230513 
*/ 
public class ChartTable { 

    private static final Random R = new Random(); 
    private static final int N = 5000; 
    private static final int W = 200; 
    private static final int H = W; 

    private void display() { 
     JFrame f = new JFrame("ChartTable"); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     DefaultTableModel model = new DefaultTableModel(
      new String[]{"", "", "", ""}, 0) { 
      @Override 
      public Class<?> getColumnClass(int columnIndex) { 
       return ChartPanel.class; 
      } 
     }; 
     for (int r = 0; r < 25; r++) { 
      ChartPanel[] row = new ChartPanel[4]; 
      for (int c = 0; c < row.length; c++) { 
       final XYSeries series = new XYSeries("Data"); 
       int n = R.nextInt(N); 
       for (int i = 0; i < n; i++) { 
        series.add(i, R.nextGaussian()); 
       } 
       XYSeriesCollection dataset = new XYSeriesCollection(series); 
       JFreeChart chart = ChartFactory.createXYLineChart(
        "Random " + series.getItemCount(), "Domain", "Range", dataset); 
       ChartPanel chartPanel = new ChartPanel(chart) { 
        @Override 
        public Dimension getPreferredSize() { 
         return new Dimension(W, H); 
        } 
       }; 
       row[c] = chartPanel; 
      } 
      model.addRow(row); 
     } 
     JTable table = new JTable(model) { 
      @Override 
      public Dimension getPreferredScrollableViewportSize() { 
       return new Dimension(4 * W, 2 * H); 
      } 
     }; 
     table.setDefaultRenderer(ChartPanel.class, new ChartRenderer()); 
     table.setRowHeight(W); 
     f.add(new JScrollPane(table)); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 

    private static class ChartRenderer implements TableCellRenderer { 

     @Override 
     public Component getTableCellRendererComponent(
      JTable table, Object value, boolean isSelected, 
      boolean hasFocus, int row, int column) { 
      return (ChartPanel) value; 
     } 
    } 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new ChartTable()::display); 
    } 
} 
+0

Я не могу понять одну вещь: почему в этом случае использование памяти по-прежнему очень огромный! Посмотрите на эту иллюстрацию - [link] (https://s14.postimg.org/kvg4q7bup/memory_Usage.png) Как уменьшить использование памяти до небольших значений? –

+0

Я разработал выше. – trashgod

+0

Но почему рост памяти растет? После запуска апплета он не большой, менее 200 Мб, но после прокрутки, изменения размера и т. Д. Использование памяти достигает значений более 600 Мб. Зачем? –

Смежные вопросы