Java mmap не работает на Android с "mmap не удалось: ENOMEM (Out of memory)"

Отображение памяти большого файла на Android в Java работает хорошо. Но при сопоставлении более чем 1,5 ГБ в общей сложности даже при множественных вызовах отображения он терпит неудачу:

mmap failed: ENOMEM (Out of memory)

Смотрите полное обсуждение здесь. Примечание. Это не сбой на сервере Linux. Андроид: largeHeap = "true" для приложения.

Следующий код Java вызывается несколько сотен раз, запрашивая ~ 1 МБ за вызов:

ByteBuffer buf = raFile.getChannel().map(allowWrites ? FileChannel.MapMode.READ_WRITE : FileChannel.MapMode.READ_ONLY, offset, byteCount);

чтобы избежать запроса одного большого смежного фрагмента памяти, который часто бывает сложнее найти. Полный текст здесь. Имейте в виду, что удвоение размера сегмента (т.е. Размера одного вызова карты) не имеет эффекта, что означает, что он останавливается в аналогичной позиции памяти. Также важно отметить, что 2 приложения с чуть меньшим лимитом выполняют штраф (намекая на ограничение для каждого процесса).

Связанные вопросы здесь, здесь, здесь, здесь, здесь, здесь, здесь и здесь.

Будет ли использоваться несколько файлов вместо одного файла с несколькими сопоставлениями?

Я читал, что это может быть ограничение для каждого процесса для виртуального адресного пространства. Где я могу найти больше об этом? Могу ли я изменить этот параметр с помощью NDK, например. как позвонить ulimit? Может ли madvise помочь мне немного здесь?

Обновление

Смотрите мой ответ здесь для инструмента mmap, который можно использовать в Java

Ответ 1

Ваша проблема, безусловно, вызвана исчерпанием виртуального адресного пространства. Вероятно, ваша проблема воспроизводится на 32-битных Android-устройствах, где доступное адресное пространство пользователя физически ограничено 2 ГБ и не может быть удалено. (Хотя это может быть 3 ГБ (маловероятно), и он настроен во время процесса сборки ОС). Вероятно, ~ 500 МБ используется для системных библиотек, JVM и его кучи. И вам доступно 1,5 ГБ.

Единственный способ в этой ситуации - ИМО - сохранить отображение только разделов файлов, которые действительно используются сейчас, и как можно скорее отключить неиспользуемые. Вы можете использовать какое-то скользящее окно, в котором только малая часть файла будет сопоставлена ​​с памятью, а когда вы закончите - отмените эту часть, продвиньте положение своего окна и нарисуйте это обновленное окно и так далее.

Также, когда вы набираете весь большой файл, ваш процесс становится привлекательной жертвой убийцы системы Out-Of-Memory. Потому что, когда вы читаете такой отображаемый файл, потребление физической памяти увеличивается, и в какой-то момент процесс будет убит.

Ответ 2

Поскольку мы не можем увеличить лимит виртуальных адресов на Android через API (для которого не нужен root-доступ), я еще не видел этого в исходном коде Android. Единственное возможное решение, которое я вижу, - реализовать вид кеша, который разбивает сегменты на доступ и выпускает более старые сегменты, если определенное количество сегментов уже помечено. Это означает, что мы делаем работу, которую ОС обычно делает для нас автоматически, что немного уродливо.

Чтобы заставить его работать под управлением Android, можно использовать этот ответ /util -mmap. Надеюсь, кто-то сможет реализовать такой MMAP-кеш для Android в какой-то момент, может быть, даже нас:)

Ответ 3

попробуйте добавить largeHeap в ваш манифест. Возможно, это работает.