2016-11-14 49 views
1

我一直與我的項目的同時,其在某些點計算大圖像的直方圖(高達40Mpx,但一般圍繞10-20Mpx照片的工作。我總是用「outOfMemoryError java堆空間」計算直方圖,內存與時間的交易?

筆記本電腦與16GB的RAM,我沒有注意到任何問題今天我切換到一個6GB的內存筆記本電腦,並與17Mpx的照片這個例外開始出現時,我正在計算一個直方圖

我已經切換到這種計算方式這是因爲它比遍歷所有像素並獲得每個像素中的所有顏色更快。

你對如何編寫這樣的建議有什麼建議?一個代碼?

如果我想讓程序更快,我想我需要使用更多的RAM(這個大的double []對象)。如果電腦有足夠的內存不會有任何問題,程序將運行平穩,但如果電腦沒有這麼多的RAM,它只會崩潰,並使程序無用。

因此,我應該用「手動」遍歷所有像素並使其「更安全」以「較慢的方式」編寫代碼?

或者我做錯了什麼,這兩件事情可以在同一時間完成?

這是一段代碼是這樣的OutOfMemoryError發生:

// 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在評論中建議的選項解決問題。

結果使用Windows 10 32位和3,5GB RAM使用@TheConstructor優化,在虛擬機:

  • 小於-Xmx1444m將運行異常
  • 用更少的優化後優化前比-Xmx824m將運行異常

這是我在默認情況下:

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) 

這是大約268MB,並且我可以通過此計算機中的命令設置的最大值爲1.5GB。我發現奇怪的是,沒有其他任何程序打開整個Windows與任何其他程序需要2GB的3.5GB。

+3

一個快速的回答是:增加你的程序使用的內存,但它不解決您的算法優化。爲了運行這個,你可以試試:-Xms1024m(你必須檢查你的JDK允許多少RAM,通常取決於服務器或客戶端版本),後面跟-XX:MaxPermSize = 128m。這裏你是對參數的解釋:https://blog4jose.wordpress.com/2009/02/02/memory-permgen-classloader/ – sirandy

+1

你是否指定了'-Xmx' JVM選項?如果不是,請先嚐試一下。 – Andreas

回答

3

最終我猜你需要指定一個正確大小的-Xmx-XX:MaxHeapSize參數給你的java-調用。默認值來自可用內存並限制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,您也可以使用(double[]) null作爲參數而不是buffer,並讓getSamples爲您分配數組。

如果精度無關緊要,也可以使用來切換double[],這可以節省一半的內存。

+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值進行協調以產生正確的結果,或者是否應該將值加上然後再除以3. – TheConstructor

+0

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

3

如果可能,最好減少對象創建,以便減少堆空間內存。如果所有的對象都在你的應用強制性的,然後使用命令行參數在運行應用程序:

java -Xms<size>  set initial Java heap size 

(OR) 

java -Xmx<size>  set maximum Java heap size 
+0

我認爲它通常都是。我盡我所能在我的回答中優化了代碼片段配置。 – TheConstructor

+0

是啊...你的優化和良好。 – Anands23

相關問題