2016-11-14 8 views
1

私のプロジェクトでは、ある時点で大きな画像のヒストグラムを計算しています(最大40Mpxの写真で作業しますが、一般的に約10-20Mpxです)。"outOfMemoryError java heap space"ヒストグラムの計算、RAMとTimeの取引?

私は常に私はヒストグラムを計算していたときにこの例外が現れ始めました。

私はこの計算方法に切り替えましたなぜなら、すべてのピクセルに対して繰り返し処理を行い、各ピクセルのすべての色を取得するよりも高速だったからです。

コード?

私がプログラムを高速化しようと思えば、私はもっと多くのRAM(この大きなdouble []オブジェクト)を使う必要があると思います。 PCに十分なRAMがある場合、問題はなく、プログラムはスムーズに実行されますが、PCにこのようなRAMがないと、クラッシュしてプログラムが役に立たなくなります。

すべてのピクセルを手作業で反復処理してコードを「より遅く」書き、「安全」にする必要がありますか?

私は間違ったことをしていますか、どちらも同時に行うことができますか?

// dataset 
    dataset = new HistogramDataset(); 
    final int w = image.getWidth(); 
    final int h = image.getHeight(); 
    double[] r = new double[w * h]; //Here some PC's with not enough RAM will crash 
    double[] s = new double[w * h]; 
    double[] t; 
    r = raster.getSamples(0, 0, w, h, 0, r); 
    s = r; 
    dataset.addSeries(lang.getString("HistogramRGB.String.red"), r, BINS); 
    r = raster.getSamples(0, 0, w, h, 1, r); 
    t = new double[r.length + s.length]; //Add R+G 
    System.arraycopy(s, 0, t, 0, s.length); 
    System.arraycopy(r, 0, t, s.length, r.length); 
    dataset.addSeries(lang.getString("HistogramRGB.String.green"), r, BINS); 
    r = raster.getSamples(0, 0, w, h, 2, r); 
    s = new double[r.length + t.length]; //Add R+G+B 
    System.arraycopy(t, 0, s, 0, t.length); 
    System.arraycopy(r, 0, s, t.length, r.length); 
    dataset.addSeries(lang.getString("HistogramRGB.String.blue"), r, BINS); 
    dataset.addSeries(lang.getString("HistogramRGB.String.brigthness"), s, BINS); 

    // chart 
    chart = ChartFactory.createHistogram(lang.getString("HistogramRGB.String.histogram"), "", 
      "", dataset, PlotOrientation.VERTICAL, false, true, false); 

更新: を-Xmxはコメントで提案オプションを使用しても問題が解決し

これはこれはOutOfMemoryErrorが発生したコードの一部です。少ないと最適化の後に例外

  • を実行します-Xmx1444m未満で最適化する前に

    • :窓10 32ビットと3,5GBラムを使用して、仮想マシンで、@TheConstructorの最適化を使用して

      結果

      java -XX:+PrintFlagsFinal -version | findstr HeapSize 
      uintx ErgoHeapSizeLimit       = 0         {product} 
      uintx HeapSizePerGCThread      = 67108864       {product} 
      uintx InitialHeapSize       := 16777216       {product} 
      uintx LargePageHeapSizeThreshold    = 134217728       {product} 
      uintx MaxHeapSize        := 268435456       {product} 
      java version "1.8.0_111" 
      Java(TM) SE Runtime Environment (build 1.8.0_111-b14) 
      Java HotSpot(TM) Client VM (build 25.111-b14, mixed mode, sharing) 
      
      :-Xmx824mは例外

    を実行するよりも、これは私がデフォルトで持っているものです

    これは約268MBで、このコンピュータのコマンドで設定できる最大値は1,5GBです。他のプログラムを使ってウィンドウ全体を開くことなく、3.5GBの2GBが必要なことは奇妙なことです。

  • +3

    あなたのプログラムで使用されているメモリを増やしても、アルゴリズムの最適化は解決されません。これを実行するには、-Xms1024mを試してみてください(あなたのJDKが許すRAMの量を確認しなければなりません。一般的には、サーバまたはクライアントのバージョンによって異なります)。-XX:MaxPermSize = 128m。ここでは、パラメータの説明です:https://blog4jose.wordpress.com/2009/02/02/memory-permgen-classloader/ – sirandy

    +1

    '-Xmx' JVMオプションを指定しましたか?そうでない場合は、まずそれを試してください。 – Andreas

    答えて

    3

    最終的にはjava -allに正しいサイズの-Xmxまたは-XX:MaxHeapSize引数を指定する必要があります。デフォルトは利用可能なメモリから派生し、Javaが使用できるメモリ量を制限します。実際のサイズを把握してみてください。あなたは例えば試みることができます-Xmx2g-Xmxcan be found inside documentation

    tを削除し、sのスキップ初期化を省略することができます。私はそれがここにすべての問題を解決することはできませんと思いますが、私の修正です:

    // dataset 
        dataset = new HistogramDataset(); 
        final int w = image.getWidth(); 
        final int h = image.getHeight(); 
        double[] buffer = new double[w * h]; 
        double[] rgb; 
    
        buffer = raster.getSamples(0, 0, w, h, 0, buffer); 
        rgb = Arrays.copyOf(buffer, buffer.length * 3); // copy as otherwise it gets overwritten in next getSamples 
        dataset.addSeries(lang.getString("HistogramRGB.String.red"), buffer, BINS); 
    
        buffer = raster.getSamples(0, 0, w, h, 1, buffer); 
        System.arraycopy(buffer, 0, rgb, buffer.length, buffer.length); //Add G 
        dataset.addSeries(lang.getString("HistogramRGB.String.green"), buffer, BINS); 
    
        buffer = raster.getSamples(0, 0, w, h, 2, buffer); 
        System.arraycopy(buffer, 0, rgb, buffer.length * 2, buffer.length); //Add B 
        dataset.addSeries(lang.getString("HistogramRGB.String.blue"), buffer, BINS); 
    
        dataset.addSeries(lang.getString("HistogramRGB.String.brigthness"), rgb, BINS); 
    
        // chart 
        chart = ChartFactory.createHistogram(lang.getString("HistogramRGB.String.histogram"), "", "", dataset, 
          PlotOrientation.VERTICAL, false, true, false); 
    

    addSeriesが供給されたデータのコピーを作成するかどうかにもよりますが、おそらくgetSamplesに各呼び出しの前buffer新しい配列を割り当てる必要があります。私が正しく推測すれば、Raster#getSamplesだと、bufferの代わりに(double[]) nullを引数として使用し、getSamplesに配列を割り当てさせることもできます。

    精度があまり重要でない場合は、double[]float[]に切り替えると、メモリの半分を節約できます。

    +1

    ['getSamples()'](http://www.jfree.org/jfreechart/api/javadoc/src-html/org/jfree/data/statistics/HistogramDataset.html#line.134)はデータをコピーします。あらかじめ割り当てられた配列は安全にリサイクルできます。完全な例がここに表示されています(http://stackoverflow.com/a/28519356/230513)。 – trashgod

    +0

    @trashgodもし 'addSeries'がデータをコピーするならば、残りの質問はRGB値をconcatinatingすることで正しい結果が得られるかどうか、あるいは値を加算して3で割り切れるかどうかです。 – TheConstructor

    +0

    [_brightness_ ](http://stackoverflow.com/q/596216/230513)。 – trashgod

    3

    可能であれば、オブジェクトの作成を減らしてヒープ領域のメモリを削減する方がよい場合があります。

    +0

    私はそれがしばしば両方であると思います。私は自分の答えでスニペットの割り当てを最適化するために最善を尽くしました。 – TheConstructor

    +0

    ええ、あなたのものは最適化されています。 – Anands23

    関連する問題