【问题标题】:"outOfMemoryError java heap space" calculating histogram, trade of RAM vs Time?“outOfMemoryError java heap space”计算直方图,RAM 与时间的交易?
【发布时间】:2017-03-28 16:30:45
【问题描述】:

我一直在使用我的项目一段时间,该项目有时会计算大图像的直方图(使用高达 40Mpx 的照片,但通常在 10-20Mpx 左右。

我一直使用 16GB RAM 的笔记本电脑,但没有发现任何问题。今天我换了一台 6GB 内存的笔记本电脑,当我计算直方图时,有了 17Mpx 的照片,这个异常开始出现。

我改用这种计算方式是因为它比遍历所有像素并获取每个像素中的所有颜色要快。

您对我应该如何编写这样的代码有什么建议?

如果我想让程序更快,我想我需要使用更多的 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);

更新: 使用 cmets 中建议的选项 -Xmx 可以解决问题。

使用 @TheConstructor 优化的结果,在使用 windows 10 32 位和 3,5GB ram 的虚拟机中:

  • 优化前小于 -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。我觉得奇怪的是,没有任何其他程序打开整个窗口需要 3.5GB 中的 2GB。

【问题讨论】:

  • 一个快速的答案是:增加程序使用的内存,但这并不能解决您的算法优化问题。要运行它,您可以尝试:-Xms1024m(您必须检查 JDK 允许多少 RAM,通常取决于服务器或客户端版本),然后是 -XX:MaxPermSize=128m。这里是对参数的解释:blog4jose.wordpress.com/2009/02/02/memory-permgen-classloader
  • 您是否指定了-Xmx JVM 选项?如果没有,请先尝试。

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


【解决方案1】:

最终我猜你需要为你的java-call 指定一个正确大小的-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,您也可以使用(double[]) null 作为参数而不是buffer,并让getSamples 为您分配数组。

如果精度不太重要,您还可以将double[] 切换为float[],这样可以节省一半的内存。

【讨论】:

  • getSamples () 复制数据,因此可以安全地回收预分配的数组;一个完整的例子显示在here
  • @trashgod 所以如果addSeries 复制数据,剩下的问题可能是连接 RGB 值是否会产生正确的结果,或者是否应该添加这些值然后除以 3。
  • 取决于brightness的定义。
  • 谢谢,您的回答非常有用,正如我在问题更新中所写的那样。 @trashgod我不确定brightness是否是英语中的正确词,但我希望我的直方图有一个与人眼(光度学)无关的经验测量(辐射测量),所以我认为它应该是正确的。
  • @nck 很高兴我能帮上忙。如果它解决了您的问题,那么一切正常。我只是想知道。
【解决方案2】:

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

java -Xms<size>        set initial Java heap size 

(OR)

java -Xmx<size>        set maximum Java heap size

【讨论】:

  • 我认为通常两者兼而有之。我尽力在我的回答中优化 sn-p 分配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-07
  • 2015-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多