BitmapFactory OOM сводит меня с ума - PullRequest
       49

BitmapFactory OOM сводит меня с ума

30 голосов
/ 24 декабря 2009

Я много искал и знаю много других людей. испытывают те же проблемы с памятью OOM с BitmapFactory. мой приложение показывает только общую доступную память 4 МБ, используя Runtime.getRuntime ().totalMemory(). Если ограничение составляет 16 МБ, то почему не общее память растет, чтобы освободить место для растрового изображения? Вместо этого выдается ошибка.

Я тоже не понимаю, что если у меня 1,6 МБ свободной памяти в соответствии на Runtime.getRuntime().freeMemory() почему я получаю сообщение об ошибке "VM не позволит нам выделить 614400 байт "? Мне кажется, у меня есть много доступная память.

Мое приложение завершено, за исключением этой проблемы, которая исчезает, когда я перезагрузите телефон, чтобы мое приложение работало единственно. я использую HTC Hero для тестирования устройств (Android 1.5).

На данный момент я думаю, что единственный способ обойти это как-то избегать использования BitmapFactory.

У кого-нибудь есть идеи или объяснения, почему ВМ не будет выделить 614 КБ при наличии 1,6 МБ свободной памяти?

Ответы [ 5 ]

45 голосов
/ 31 марта 2011

[Обратите внимание, что (как указывает CommonsWare ниже) весь подход в этом ответе применяется только до 2.3.x включительно (Gingerbread) включительно. Начиная с Honeycomb Bitmap данные размещаются в куче виртуальной машины.]

Данные растрового изображения не размещаются в куче виртуальной машины. Существует ссылка на него в куче виртуальной машины (которая мала), но фактические данные выделяются в собственной куче базовой графической библиотекой Skia.

К сожалению, хотя определение BitmapFactory.decode ... () говорит, что оно возвращает ноль, если данные изображения не могут быть декодированы, реализация Skia (или, скорее, клей JNI между кодом Java и Skia) регистрирует сообщение Вы видите («VM не позволяет нам выделять байты xxxx»), а затем выдает исключение OutOfMemory с вводящим в заблуждение сообщением «Размер растрового изображения превышает бюджет VM».

Проблема не в куче виртуальных машин, а в собственной куче. Естественная куча распределяется между запущенными приложениями, поэтому объем свободного места зависит от того, какие другие приложения работают, и от их использования растрового изображения. Но, учитывая, что BitmapFactory не вернется, вам нужно определить, будет ли вызов успешным, прежде чем вы его сделаете.

Существуют подпрограммы для контроля размера собственной кучи (см. Методы getNative класса Debug). Однако я обнаружил, что getNativeHeapFreeSize () и getNativeHeapSize () не являются надежными. Поэтому в одном из моих приложений, которое динамически создает большое количество растровых изображений, я делаю следующее.

Размер собственной кучи зависит от платформы. Поэтому при запуске мы проверяем максимально допустимый размер кучи виртуальной машины, чтобы определить максимально допустимый собственный размер кучи. [Магические числа были определены при тестировании на 2.1 и 2.2 и могут отличаться на других уровнях API.]

long mMaxVmHeap     = Runtime.getRuntime().maxMemory()/1024;
long mMaxNativeHeap = 16*1024;
if (mMaxVmHeap == 16*1024)
     mMaxNativeHeap = 16*1024;
else if (mMaxVmHeap == 24*1024)
     mMaxNativeHeap = 24*1024;
else
    Log.w(TAG, "Unrecognized VM heap size = " + mMaxVmHeap);

Тогда каждый раз, когда нам нужно вызвать BitmapFactory, мы предшествуем вызову проверкой формы.

long sizeReqd        = bitmapWidth * bitmapHeight * targetBpp  / 8;
long allocNativeHeap = Debug.getNativeHeapAllocatedSize();
if ((sizeReqd + allocNativeHeap + heapPad) >= mMaxNativeHeap)
{
    // Do not call BitmapFactory…
}

Обратите внимание, что heapPad - это магическое число, учитывающее тот факт, что a) отчет о размере собственной кучи является "мягким" и b) мы хотим оставить некоторое пространство в собственной куче для других приложений. В настоящее время мы работаем с пэдом 3 *1024* 1024 (т.е. 3 Мб).

2 голосов
/ 01 апреля 2012

Похоже, что проблемы, указанные в Ответ Торида , были решены в более поздних версиях Android.

Однако, если вы используете кэш изображений (специализированный или даже обычный HashMap), эту ошибку довольно легко получить, создав утечку памяти.

По моему опыту, если вы непреднамеренно удерживаете свои ссылки Bitmap и создаете утечку памяти, возникает ошибка OP (ссылка на BitmapFactory и нативные методы) - это то, что приведет к сбою вашего приложения (до ICS - 14 и +?)

Чтобы избежать этого, заставьте вас «отпустить» свои растровые изображения. Это означает использование SoftReferences на последнем уровне вашего кэша, чтобы растровые изображения могли собирать мусор из него. Это должно работать, но если у вас все еще возникают сбои, вы можете попытаться явно пометить определенные битовые карты для сбора, используя bitmap.recycle(), просто не забывайте никогда не возвращать битовый массив для использования в ваше приложение, если bitmap.isRecycled().

Кроме того, LinkedHashMaps - отличный инструмент для простой реализации довольно хороших структур кэша, особенно если вы объединяете жесткие и мягкие ссылки, как в в этом примере (строка 308) ... но использование жестких ссылок также помогает вам попадать в ситуации утечки памяти, если вы запутались.

2 голосов
/ 24 декабря 2009

1,6 МБ памяти кажется большим, но может случиться так, что память настолько сильно фрагментирована, что не сможет выделить такой большой блок памяти за один раз (все же это звучит очень странно).

Одной из распространенных причин использования OOM при использовании ресурсов изображений является распаковка изображений JPG, PNG, GIF с действительно высоким разрешением. Вы должны иметь в виду, что все эти форматы довольно хорошо сжаты и занимают очень мало места, но как только вы загружаете изображения в телефон, память, которую они собираются использовать, выглядит как width * height * 4 bytes. Кроме того, когда начинается декомпрессия, необходимо загрузить несколько других вспомогательных структур данных для этапа декодирования.

0 голосов
/ 02 декабря 2011

Хотя это довольно высокий уровень ответа, проблема для меня заключалась в использовании аппаратного ускорения во всех моих представлениях. Большинство моих представлений имеют пользовательские манипуляции с растровыми изображениями, которые я полагал источником большого размера собственной кучи, но фактически при отключении аппаратного ускорения использование собственной кучи было сокращено в 4 раза.

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

0 голосов
/ 19 июля 2011

Хотя обычно нет смысла отлавливать ошибку, потому что обычно они генерируются только виртуальной машиной, но в данном конкретном случае ошибка генерируется кодом jni glue, поэтому очень просто обрабатывать случаи, когда вы не можете загрузить изображение: просто перехватите OutOfMemoryError.

...