2016-11-14 45 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

相关问题