Android NDK mmap вызывается на 32-битных устройствах после обновления до Lollipop

Я пытаюсь захватить 784 Мбайт памяти. Да, я знаю, что это много для 32-битного телефона, но до Android 5.0 работал следующий вызов:

mmap(0, 0x31000000, PROT_NONE, MAP_ANON | MAP_SHARED, -1, 0);

Однако на трех разных устройствах от разных производителей обновление до Android 5.0 нарушило это. Я предполагаю, что это некоторое изменение в функции распределения памяти в 5.0; возможно, нужно передать разные флаги?

Здесь сообщение об ошибке возвращается в logcat:

E/libc﹕ mmap fail (pid 9994, tid 10125, size 822083584, flags 0x21, errno 12(Out of memory))

Ответ 1

В момент, когда mmap() завершается сбой, откройте /proc/self/maps и скопируйте содержимое в файл temp, затем просмотрите файл в редакторе. Вы должны увидеть кучу записей вроде этого:

12e01000-42c00000 ---p 00201000 00:04 11639      /dev/ashmem/dalvik-main space (deleted)
55281000-5d500000 r--s 00000000 00:16 61         /storage/sdcard1/blah
5d500000-67e80000 rw-p 00000000 00:00 0          [anon:libc_malloc]
67ea4000-682cc000 r-xp 00000000 b3:17 114807     /system/vendor/lib/libsc-a3xx.so
682cc000-682f4000 r--p 00427000 b3:17 114807     /system/vendor/lib/libsc-a3xx.so

Числа слева представляют собой диапазоны виртуальных адресов (начало/конец) для процесса. Когда вы создаете новое сопоставление, оно должно соответствовать пробелу между отображениями.

В приведенном выше примере есть хороший большой промежуток между концом первой записи в 0x42c00000 и началом следующего в 0x55281000. Это около 294 МБ. Между двумя соседями нет места, и только после этого.

Если вы посмотрите на карту процессов и не найдете достаточно большого пробела для хранения вашего файла, у вас есть свой ответ. Область между 0x00000000 и 0xbfffffff обычно доступна для 32-разрядных приложений, но инфраструктура приложения использует ее много. (Верхняя 1 ГБ отображается на ядро.)

Я предполагаю, что некоторая комбинация ASLR и изменения в том, как виртуальная память распределена в Lollipop, привели к этой проблеме. На карте, прикрепленной к этому аналогичному вопросу, наибольший разрыв был около 300 МБ. Есть два больших "далвика" региона, один 768 МБ (на 12e01000), один 1,2 ГБ (на 84d81000). (Так как вы используете Lollipop, это на самом деле связано с ART, а не с Dalvik, но, по-видимому, этот ярлык застрял.)

Одна из возможностей заключается в том, что у АРТ более высокие требования к виртуальной памяти, чем у Dalvik, и большие ассигнования затрудняют приложения для получения областей с крупными картами. Также возможно, что АРТ чрезмерно распределяется из-за ошибки. Вы можете протестировать на Marshmallow, чтобы убедиться, что что-то исправлено.

В любом случае вы не можете создать сопоставление, если область смежных областей виртуальной памяти не будет достаточно большой, чтобы ее удерживать. С использованием рамок приложения и огромными распределениями АРТ, рассмотренными в другом вопросе, сопоставление 768 МБ было бы невозможно, даже если виртуальное адресное пространство не было фрагментировано. Вам нужно будет отобразить меньшие разделы файла и, возможно, отобразить их, когда вы работаете, чтобы освободить место.

Возможно, стоит записать ошибку b.android.com. Прикрепите копию файла карты процесса и укажите версию Android и устройства.

Более подробно об интерпретации /proc/maps output см., например, этот ответ.

Ответ 2

  • Скомпилируйте свою библиотеку приложений в соответствии с леденцом и повторите попытку.
  • Обновите свой SDK
  • Обязательно установите целевую платформу на леденец в настройках вашего приложения.
  • Уменьшить выделенную память и снова проверить

Ответ 3

Вы пробовали вариант largeHeap?

В очень особых ситуациях вы можете запросить больший размер кучи на установка атрибута largeHeap в значение "true" в манифесте  тег. Если вы это сделаете, вы можете вызвать getLargeMemoryClass(), чтобы получить оценку большого размера кучи.

https://developer.android.com/training/articles/memory.html#CheckHowMuchMemory

Подробнее:

https://developer.android.com/guide/topics/manifest/application-element.html#largeHeap