Избегайте фрагментации памяти при выделении большого количества массивов в Java

Я разрабатываю приложение в Java, который работает на устройствах Windows Mobile. Для достижения этого, мы использовали JVM Esmertec JBed, которая не прекрасна, но мы застреваем с нею на данный момент. Недавно мы получали жалобы от клиентов о OutOfMemoryErrors. После большой игры вокруг с вещами я обнаружил, что устройство имеет много свободной памяти (приблизительно 4 МБ).

OutOfMemoryErrors всегда происходят в той же точке в коде, и это при расширении StringBuffer для добавления некоторых символов к нему. После добавления некоторого входа вокруг этой области я нашел, что мой StringBuffer имел приблизительно 290 000 символов в нем со способностью приблизительно 290 500. Стратегия расширения внутреннего символьного массива состоит в том, чтобы просто удвоить размер, таким образом, это попыталось бы выделить массив приблизительно 580 000 символов. Я распечатал использование памяти в это время также и нашел, что оно использовало приблизительно 3.8 МБ общего количества приблизительно 6.8 МБ (хотя я видел, что общая доступная память время от времени повышается приблизительно до 12 МБ, таким образом, существует много комнаты для расширения). Таким образом, это в этой точке, что приложение сообщает о OutOfMemoryError, который не имеет большого количества смысла, данного, насколько там все еще свободно.

Я начал думать об операции приложения до этой точки. В основном то, что происходит, я анализирую использование XML-файла MinML (маленький Синтаксический анализатор Саксофона XML). Одно из полей в XML имеет о 300k символах в нем. Синтаксический анализатор передает данные потоком из диска, и по умолчанию это загружает только 256 символов за один раз. Таким образом, когда это достигает рассматриваемого поля, синтаксический анализатор назовет 'символы ()' методом обработчика более чем 1 000 раз. Каждый раз это создаст новый символ [] содержание 256 символов. Обработчик просто добавляет эти символы к StringBuffer. Начальный размер по умолчанию StringBuffer - только 12, поэтому поскольку символы добавляются к буферу, он оказывается перед необходимостью расти неоднократно (каждый раз создавая новый символ []).

Мое предположение от этого было то, что возможно что, в то время как существует достаточно свободной памяти, так как предыдущий символ [] s может быть собран "мусор", возможно, нет никакого непрерывного блока памяти, достаточно большого для установки новому массиву, который я пытаюсь выделить. И возможно JVM не достаточно умна для расширения размера "кучи", потому что это глупо и думает, что нет никакой потребности потому что, по-видимому, существует достаточно свободной памяти.

Таким образом, мой вопрос: кто-либо имеет опыт этой JVM и мог бы быть в состоянии окончательно подтвердить или опровергнуть мои предположения о выделении памяти? И также, у кого-либо есть какие-либо идеи (предполагающий, что мои предположения корректны) о том, как улучшить выделение массивов так, чтобы память не становилась фрагментированной?

Примечание: вещи я уже попробовал:

  • Я увеличил начальный размер массива StringBuffer и меня increaed размер чтения синтаксического анализатора так, чтобы он не должен был создавать столько массивов.
  • Я изменил стратегию расширения StringBuffer так, чтобы, как только это достигло порога определенного размера, который это только развернет на 25%, а не 100%.

Выполнение обеих из этих вещей помогло немногому, но поскольку я увеличиваю размер данных XML, входящих, я все еще получаю OutOfMemoryErrors в довольно низком размере (приблизительно 350 КБ).

Другая вещь добавить: все это тестирование было выполнено на устройстве с помощью рассматриваемой JVM. Если я выполняю тот же код рабочего стола с помощью Java SE 1,2 JVM, у меня нет проблем, или по крайней мере я не получаю проблему, пока мои данные не достигают приблизительно 4 МБ в размере.

Править:

другая вещь, которую я только что попробовал, который помог немного, я установил Xms на 10M. Таким образом, это заканчивает проблему JVM, не расширяющей "кучу", когда она должна и позволять мне обрабатывать больше данных, прежде чем ошибка произойдет.

13
задан DaveJohnston 15 January 2010 в 11:12
поделиться

6 ответов

Просто для обновления моего собственного вопроса я обнаружил, что лучшее решение было установить минимальный размер кучи (я установил его на 10 м). Это означает, что JVM никогда не должен решать, следует ли расширять кучу и, следовательно, оно никогда (до сих пор в тесте) умирает с OutofMemoryError, хотя у него должно быть много места. Пока что в тесте мы смогли утроить объем данных, которые мы разбираемся без ошибки, и мы, вероятно, могли бы пойти дальше, если нам действительно нужно.

Это немного взлома для быстрого решения, чтобы сохранить существующие клиенты счастливыми, но теперь мы смотрим на другой JVM, и я сообщу обратно с обновлением, если JVM обрабатывает это Scneario.

2
ответ дан 2 December 2019 в 02:05
поделиться

Из того, что я знаю о JVMs, фрагментация никогда не должна быть проблемой, которую вы имеете для решения. Если больше нет комнаты для выделения - должен ли из-за фрагментации или не - сборщик "мусора" работать, и GCs также обычно сжимают данные для решения проблем фрагментации.

Для подчеркивания - вы только вытаскиваете "из памяти" ошибки после , GC был, работал, и все еще достаточно памяти не могло быть освобождено.

я вместо этого попытался бы вырыть больше в опции для определенной JVM, которую вы выполняете. Например, сборщик "мусора" "копирования" использует только половину доступной памяти за один раз, таким образом изменение вашей VM для использования чего-то еще могло возможно освободить половину памяти.

я действительно не предполагаю, что ваша VM использует простое копирование GC, я просто предлагаю зондировать это на уровне VM.

1
ответ дан 2 December 2019 в 02:05
поделиться

Удали ли вы получить сброс кучи с устройства?

Если вы получите свалку кучи, и он находится в совместимом формате, некоторые анализаторы памяти Java дают информацию о размере смежных блоки памяти. Я умер, увидев эту функционал в анализаторе IBM Heap http://www.alphaworks.ibm.com/tech/heapanalyzer , но и проверьте более актуальный анализатор памяти Eclipse http: // www.eclipse.org/mat/

If У вас есть возможность модификации файла XML, который, вероятно, будет самым быстрым выходом. XML Parsing в Java всегда скорее в памяти, и 300K довольно много для одного поля. Вместо этого вы можете попытаться отделить это поле в отдельный файл без XML.

0
ответ дан 2 December 2019 в 02:05
поделиться

Я сделал две вещи на том же шаге. Не уверенный, если один или оба разрешили проблему:

  1. Добавленный [DataContract (Пространство имен = "Мой. Общий. Assemblys. Пространство имен")] к одно из классов в совместно используемой сборке.
  2. Перезапущенная Бета-версия 2
-121 VS 2010---2949006-

я не уверен, выделяются ли эти StringBuffers в MinML - если так, я предполагаю, что у вас есть источник для него? Если вы делаете, то, возможно, поскольку вы сканируете последовательность, если последовательность достигает определенной длины (говорят, 10 000 байтов), вы могли бы смотреть вперед, чтобы определить точную длину последовательности и перераспределить буфер к тому размеру. Это является уродливым, но это сохранило бы память. (Это может даже быть быстрее, чем не выполнение предвидений, так как вы потенциально сохраняете много перераспределений.)

, Если вы не делаете , имеют доступ к источнику MinML, затем я не уверен, что время жизни StringBuffer относительно XML-документа. Но это предложение (хотя это еще более ужасно, чем последнее) могло бы все еще работать: Так как вы получаете XML от диска, возможно, вы могли предварительно проанализировать его, использование (говорит) что синтаксический анализатор SAX, только чтобы получить размер полей последовательности и выделить StingBuffers соответственно?

0
ответ дан 2 December 2019 в 02:05
поделиться

Я думаю, у вас есть много памяти, но создаем огромное количество эталонных объектов. Попробуйте эту статью: https://web.archive.org/web/1/http://articles.techrepublic%2eCom%2Ecom/5100-10878_11-1049545.html?tag=rbxccnbtr1 Для получения дополнительной информации Отказ

0
ответ дан 2 December 2019 в 02:05
поделиться

Может быть, вы можете попробовать VTD свет. Это кажется более эффективной памяти, чем SAX. (Я знаю, что это огромное изменение.)

2
ответ дан 2 December 2019 в 02:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: