" outOfMemoryError java堆空间"计算直方图,RAM与时间的交易?

时间:2016-11-14 22:25:55

标签: java performance swing out-of-memory histogram

我一直在处理我的项目一段时间,在某些时候计算大图像的直方图(使用高达40Mpx的照片,但一般大约10-20Mpx。

我一直在使用配备16GB内存的笔记本电脑,我没有注意到任何问题。今天我改用6GB ram笔记本电脑和17Mpx的照片,这个Exception在我计算直方图时开始显现。

我已经改用这种方式来计算它,因为它比迭代所有像素并获得每个像素中的所有颜色更快。

您对如何编写此类代码有何建议?

如果我想让程序更快,我想我需要使用更多的RAM(这个大的double []对象)。如果PC有足够的RAM,那么就不会出现任何问题而且程序运行顺畅,但如果PC没有这么大的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。我觉得很奇怪,没有任何其他东西打开整个窗口与任何其他程序需要2GB的3.5GB。

2 个答案:

答案 0 :(得分:3)

最后,我猜您需要为-Xmx - 电话指定正确大小的-XX:MaxHeapSizejava参数。默认值来自可用内存,并限制Java可以使用的内存量。试着弄清楚工作规模。你可以尝试例如-Xmx2g。关于-Xmx can 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是否创建所提供数据的副本,您可能需要在每次调用buffer之前为getSamples分配一个新数组。如果我正确猜到它是Raster#getSamples,您也可以使用(double[]) null作为参数而不是buffer,让getSamples为您分配数组。

如果精度无关紧要,你也可以用double[]切换float[],这可以节省一半的内存。

答案 1 :(得分:3)

如果可能,最好减少对象创建,以便减少堆空间内存。如果应用程序中的所有对象都是必需的,那么在运行应用程序时使用命令行参数:

java -Xms<size>        set initial Java heap size 

(OR)

java -Xmx<size>        set maximum Java heap size