2016-11-14 2 views
1

Я работал с моим проектом на некоторое время, которое в некоторых точках вычисляет Гистограмма большого изображения (Работа с фотографиями до 40Mpx, но в целом около 10-20Mpx.«outOfMemoryError java heap space» вычислительная гистограмма, торговля оперативной памятью и временем?

Я всегда с использованием ноутбук с 16 ГБ оперативной памяти, и я не заметил никаких проблем. Сегодня я переключился на ноутбук с 6 ГБ и с фотографиями 17Mpx это исключение начало появляться, когда я вычислял гистограмму.

Я переключился на этот способ вычисления потому что это было быстрее, чем повторение всех пикселей и получение всех цветов в каждом пикселе.

Каков ваш совет относительно того, как я должен писать такие код?

Если я ищу, чтобы сделать программу быстрее, я думаю, мне нужно использовать больше ОЗУ (этот большой двойной объект []). Если на ПК достаточно ОЗУ, проблем не будет, и программа будет работать бесперебойно, но если на ПК нет такой большой памяти, она просто сработает и сделает программу бесполезной.

Так должен ли я писать код «медленнее», итерации по всем пикселам вручную и сделать его «безопаснее»?

Или я делаю что-то неправильно, и обе вещи могут быть выполнены одновременно?

Это кусок кода, были это 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); 

Update: Использование опции -Xmx предложенный в комментариях решает эту проблему.

Результаты с использованием оптимизации @TheConstructor, в виртуальной машине с помощью окна 10 32bits и 3,5GB барана:

  • До оптимизации с менее чем -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) 

Это около 268 МБ, и максимум, который я могу установить по команде на этом компьютере, составляет 1,5 ГБ. Мне кажется странным, что без каких-либо других открытий все окна с любой другой программой требуют 2 ГБ 3,5 ГБ.

+3

Быстрый ответ: увеличить память, используемую вашей программой, однако она не позволяет оптимизировать алгоритм. Для этого вы можете попробовать: -Xms1024m (вы должны проверить, сколько RAM ваш JDK позволяет, в общем, зависит от версии сервера или клиента), а затем -XX: MaxPermSize = 128m. Здесь вы являетесь объяснением параметров: https://blog4jose.wordpress.com/2009/02/02/memory-permgen-classloader/ – sirandy

+1

Вы указали опцию '-Xmx' JVM? Если нет, попробуйте это первым. – Andreas

ответ

3

В конечном счете, я предполагаю, что вам нужно указать правильный размер -Xmx или -XX:MaxHeapSize аргумент для вашего java -call. Значения по умолчанию выводятся из доступной памяти и ограничивают объем памяти, которую может использовать 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 создает копию предоставленных данных, вы, возможно, потребуется назначить buffer новый массив перед каждым вызовом getSamples , Если я правильно догадался, это Raster#getSamples, вы также можете использовать (double[]) null как аргумент вместо buffer и дать 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 с правильным результатом или если значения должны быть добавлены, а затем разделены на 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

Смежные вопросы