Я занимаюсь разработкой приложения на Java, которое работает на устройствах Windows Mobile. Для достижения этой цели мы использовали JVed Esmertec JBed, которая не идеальна, но мы застряли с ней на данный момент. В последнее время мы получаем жалобы от клиентов о OutOfMemoryErrors. После долгой игры с предметами я обнаружил, что в устройстве достаточно свободной памяти (около 4 МБ).
Ошибки OutOfMemoryErrors всегда происходят в одной и той же точке кода, то есть при расширении StringBuffer для добавления к нему некоторых символов. Добавив некоторые записи в этой области, я обнаружил, что мой StringBuffer содержит около 290000 символов с емкостью около 290500. Стратегия расширения внутреннего массива символов заключается в простом удвоении размера, поэтому он будет пытаться выделить массив из около 580000 знаков. Примерно в это же время я распечатал использование памяти и обнаружил, что она использует около 3,8 МБ общей емкости около 6,8 МБ (хотя я видел, как общий объем доступной памяти время от времени увеличивался примерно до 12 МБ, поэтому есть много места для расширения). Таким образом, именно в этот момент приложение сообщает об ошибке OutOfMemoryError, которая не имеет особого смысла, учитывая, сколько еще остается свободных.
Я начал думать о работе приложения до этого момента. По сути, я анализирую XML-файл, используя MinML (небольшой синтаксический анализатор XML). В одном из полей XML содержится около 300 тыс. Символов. Парсер передает данные с диска и по умолчанию загружает только 256 символов за раз. Таким образом, когда он достигает рассматриваемого поля, парсер будет вызывать метод characters () обработчика более 1000 раз. Каждый раз будет создаваться новый символ [], содержащий 256 символов. Обработчик просто добавляет эти символы в StringBuffer. Начальный размер по умолчанию для StringBuffer составляет всего 12, поэтому, поскольку символы добавляются в буфер, ему придется увеличиваться несколько раз (каждый раз при создании нового символа []).
Мое предположение из этого состояло в том, что, возможно, хотя свободной памяти достаточно, поскольку предыдущие символы [] можно собирать мусором, возможно, нет непрерывного блока памяти, достаточно большого для размещения нового массива, который я пытаюсь выделить , И, возможно, JVM недостаточно умна, чтобы расширять размер кучи, потому что она глупая и считает, что в этом нет необходимости, потому что, очевидно, достаточно свободной памяти.
Итак, мой вопрос: есть ли у кого-нибудь опыт работы с этой JVM, и он мог бы окончательно подтвердить или опровергнуть мои предположения о распределении памяти? А также, есть ли у кого-нибудь какие-либо идеи (если мои предположения верны) о том, как улучшить распределение массивов, чтобы память не стала фрагментированной?
Примечание: вещи, которые я уже пробовал:
- Я увеличил начальный размер массива StringBuffer и увеличил размер чтения парсера, чтобы не нужно было создавать так много массивов.
- Я изменил стратегию расширения StringBuffer таким образом, чтобы при достижении определенного порога размера он расширялся только на 25%, а не на 100%.
Выполнение обеих этих задач немного помогло, но по мере увеличения размера входящих данных xml я все равно получаю OutOfMemoryErrors при довольно небольшом размере (около 350 КБ).
Еще одна вещь, которую нужно добавить: все это тестирование проводилось на устройстве, использующем соответствующую JVM. Если я запускаю тот же код на рабочем столе, используя Java SE 1.2 JVM, у меня не возникает никаких проблем, или, по крайней мере, у меня не возникает проблем, пока размер моих данных не достигнет около 4 МБ.
EDIT:
еще одна вещь, которую я только что попробовал, которая немного помогла, я установил Xms на 10M. Таким образом, это устраняет проблему, заключающуюся в том, что JVM не расширяет кучу, когда это необходимо, и позволяет мне обрабатывать больше данных, прежде чем произойдет ошибка.