2016-11-05 16 views
2

JScrollPaneを使用して一部のデータのサムネイルのチャートを作成しようとしていますが、パフォーマンスに問題があります。この例には、約100個のサムネイルチャートがあり、それぞれに5000個のサンプルがあります。私が何回もスクロールダウンしようとすると、スクロールが遅れて発生し、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

追加: 私はいくつかの誤解があると思います。

モニターによるヒープサイズ enter image description here 開始アプレットのヒープサイズはわずか125 Mbですが、それはクールです。 しかし、私はテストを開始しています:スクロールとサイズ変更、複数回、上下、上下、小さなフレーム、大きなフレーム。ヒープサイズは500Mb以上に成長!この状況は普通ではないと思います。

加算#2

実際の例:私のデータのみを2メガビット程度のサイズを持っており、90枚のチャート(各1中2シリーズ)で表さ

、1シリーズは3000個の要素が含まれています。私はスライダーで番号の列を変更する実装しました。 enter image description here

しかし、この小さなデータのヒープサイズは1.5 GB以上になります。

enter image description here

これは例えば数値列を変更する、いくつかのアクションの後に起こります 私のCPU(コア2デュオ2.2GHz)では、すべての描画テーブルに約4秒かかります!この大きな遅れでスライダーをコントロールするのは難しいです。

更新:

私は、サムネイルチャートあたり100個のサンプルに私のデータをダウンサンプリング実装しました。 これは間違いなく高速ですが、HUGEヒープサイズの問題は依然として存在します。写真では700Mb以上で、それは記録ではありません。私はイライラしています。

答えて

4

可視チャートのみをレンダリングするには、flyweight patternを使用してください。 JTablerenderersによって使用されるアプローチは、hereで概説され、下記のChartRendererに示されています。説明のために、セルが公開されるたびにデータセットが再作成されます。アプリケーションのスクロール、サイズ変更、切り替えを行い、効果を確認します。このようなレンダリングは数万個のセルに拡張されますが、各チャートではまだデータポイントがNになります。 Scrollableメソッドの実装で表示されるセルの数を、次のようにgetPreferredScrollableViewportSize()に制限することができます。

どのようにメモリ使用量を小さな値に減らすか?

そこには一般的な答えはありませんが、いくつかの戦略が参考になるかもしれない:

  • 作曲チャート早くも、可能な限りプログラムの初期化ではなく、彼らがレンダリングされている時に、以下のupdatedの例はTableModelChartPanelのインスタンスに構成します。それに対応してChartRendererがより簡単です。

  • 数千点を超えるグラフは効果的に読み取れません。大量のデータセットを切り捨て、のデータを表示することを検討してください。here

  • プラットフォームアクティビティモニタは誤解を招く恐れがあります。 profile実際の結果を確認してください。

image

アプレットを起動した後には、200 MB未満大きくはないですが、リサイズなど、スクロール後のメモリ使用量が値600以上MBに達します。どうして?以下のコードの

典型的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

私は一つのことを理解することはできません。なぜこのような場合のメモリ使用量がまだ非常に巨大な!この図を見てください - [リンク](https://s14.postimg.org/kvg4q7bup/memory_Usage.png)メモリ使用量を小さな値に減らすには? –

+0

私は上記で詳しく述べました。 – trashgod

+0

しかし、なぜメモリ使用量が増えていますか?アプレットを起動した後は、200 MB未満ではなく、スクロール後にサイズ変更などのメモリ使用量が600 Mb以上の値に達します。どうして? –

関連する問題