2016-11-05 5 views
2

JScrollPane을 사용하여 일부 데이터의 미리보기 이미지 차트를 만들려고하는데 성능 문제가 있습니다. 이 예제에는 각각 약 5000 개의 샘플이있는 약 100 개의 축소판 차트가 있습니다. 아래로 스크롤하여 여러 번 위로 이동하려고하면 스크롤이 지연되고 CPU로드가 증가하며 응용 프로그램 메모리 사용량이 500MB를 초과합니다.썸네일 차트를 만드는 올바른 방법은 무엇입니까?

데이터를 축소하지 않고도 성능상의 문제가 발생하지 않도록 할 수 있습니까?

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

} 
} 

편집 : 나는 한 가지를 이해할 수 없다 : 왜이 ​​경우 메모리 여전히 사용이 매우 큰에서! 이 그림을보십시오.

image

추가 : 나는 약간의 오해가 있다고 생각합니다.

힙 크기 by monitor visualVM enter image description here 시작 애플릿 힙 크기는 125Mb에 불과하므로 시원합니다. 하지만 이제는 테스트를 시작합니다. 여러 번 스크롤하고 크기를 조정하고, 위아래로, 위아래로, 더 작은 프레임과 더 큰 프레임으로 크기를 조정합니다. 힙 크기는 500Mb 이상 증가합니다! 나는이 상황이 정상이 아니라고 생각한다.

추가 # 2

실제 예제 :

내 데이터는 2 메가에 대한 크기와 90 개 차트에 표현 (각각 2 시리즈)가 하나의 시리즈는 3000 개 요소가 포함되어 있습니다. 슬라이더로 숫자 열을 변경하여 구현했습니다. enter image description here

그러나이 작은 데이터 힙 크기는 1.5GB 이상 증가합니다!

enter image description here

이것은 예를 들면 숫자 열을 변경, 일부 작업 후 발생 내 CPU (코어 2 듀오 2.2GHz)의 경우 모든 드로잉 테이블에 약 4 초의 시간이 걸립니다! 이 큰 지연으로 슬라이더를 제어하기가 어렵습니다.

갱신 :

내가 썸네일 차트 당 100 개 샘플 내 데이터를 다운 샘플링을 구현했습니다. 지금은 확실히 빠르지 만, 여전히 큰 힙 크기에는 문제가 있습니다. 그림에서 700Mb 이상이고 기록이 아닙니다. 나는 좌절했다.

답변

4

표시되는 차트 만 렌더링하려면 flyweight pattern을 사용하십시오. JTablerenderers에 의해 사용 된 접근법은 here으로 약술되어 있으며 아래에 표시된 ChartRenderer에 나와 있습니다. 예를 들어 셀이 공개 될 때마다 데이터 집합이 다시 만들어집니다. 스크롤, 크기 조정 및 응용 프로그램을 전환하여 효과를 확인하십시오. 이러한 렌더링은 수만 개의 셀로 확장되지만 각 차트는 여전히 N 데이터 포인트를 렌더링합니다. 아래 그림과 같이 Scrollable 메서드 구현에서 보이는 셀의 수인 getPreferredScrollableViewportSize()을 제한 할 수 있습니다.

작은 값으로 메모리 사용을 줄이려면 어떻게해야합니까?

가 더 일반적인 대답은 없지만, 몇 가지 전략이 도움이 될 수 있습니다

  • 작성 차트 이르면 가능한 프로그램 초기화에서보다는 그들이 렌더링 시간에; 아래의 updated 예제는 TableModelChartPanel 인스턴스로 구성합니다. 따라서 ChartRenderer이 더 간단합니다.

  • 몇 천 개가 넘는 차트는 효과적으로 읽을 수 없습니다. 큰 데이터 집합을 자르고 ListSelectionEvent에 대한 응답으로 전체 데이터 만 표시하는 것으로 고려하십시오 (here).

  • 플랫폼 활동 모니터가 오도 될 수 있습니다. profile 실제 결과를 확인하십시오. 이 큰 아니다 애플릿을 시작한 후

    image

미만 200 메가 있지만 스크롤 후 등의 메모리 사용량 크기 조정보다 600 메가 비트 이상의 값에 도달한다. 왜?

아래 코드의 전형적인 profiler보기는 적당한 사용 및 스크롤 garbage collection 후 예상 free/used ratio을 보여줍니다

; 귀하의 결과는 다를 수 있습니다.

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 Mb보다 크지 않지만 스크롤 한 후에는 메모리 사용량이 600 Mb 이상의 값에 도달합니다. 왜? –

관련 문제