Android: OutofMemoryError: размер растрового изображения превышает бюджет виртуальной машины, и я не вижу причин

У меня есть исключение OutOfMemory с галереей размером более 600x800 пикселей JPEG.


Окружающая среда

Я использовал галерею с изображениями JPG размером около 600x800 пикселей.

Поскольку мой контент может быть немного более сложным, чем просто изображения, я установил для каждого представления RelativeLayout, который обертывает ImageView с помощью JPG.

Чтобы «ускорить» взаимодействие с пользователем, у меня есть простой кеш из 4 слотов, который предварительно выбирает (в цикле) около 1 изображения слева и 1 изображение справа от отображаемого изображения и сохраняет их в HashMap с 4 слотами.

Платформа

Я использую AVD с 256 ОЗУ и 128 размером кучи с экраном 600x800. Это также происходит с целью Entourage Edge, за исключением того, что с устройством труднее отлаживать.


Проблема

У меня исключение:

OutofMemoryError: bitmap size exceeds VM budget

И это происходит при загрузке пятого изображения. Я попытался изменить размер кеша изображений, но он остался прежним.


Странная вещь: проблем с памятью быть не должно

Чтобы убедиться, что предел кучи очень далек от того, что мне нужно, я вначале определил фиктивный массив размером 8 МБ и оставил его без ссылок, поэтому он немедленно отправляется. Он является участником потока активности и определяется следующим образом

static { @SuppressWarnings("unused")
byte dummy[] = new byte[ 8*1024*1024 ]; }    

В результате размер кучи составляет почти 11 МБ, и все это бесплатно. Примечание Я добавил этот трюк после того, как он начал вылетать. Это делает OutOfMemory реже.

Теперь я использую DDMS. Непосредственно перед сбоем (после сбоя практически не меняется) DDMS показывает:

ID  Heap Size   Allocated   Free       %Used    #Objects
1   11.195 MB   2.428 MB    8.767 MB   21.69%   47,156  

А в таблице деталей это показывает:

Type  Count  Total Size   Smallest   Largest   Median    Average
free  1,536  8.739MB      16B        7.750MB   24B       5.825KB

Самый большой блок - 7,7 МБ. И все же LogCat говорит:

ERROR/dalvikvm-heap(1923): 925200-byte external allocation too large for this process.

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

Примечание: я записал след кучи. Если посмотреть на объем выделенных данных, не кажется, что выделено более 2 МБ. Он соответствует отчету о свободной памяти от DDMS.


  • Может быть, у меня возникла проблема, например, фрагментация кучи?
  • Как решить проблему?
  • Распространяется ли куча на все потоки?
  • Может быть, я неправильно интерпретирую показания DDMS, и на самом деле нет блока размером 900 КБ, который нужно выделить? Если да, то может ли кто-нибудь сказать мне, где я могу это увидеть?

Большое спасибо

Мейманн


person Meymann    schedule 14.06.2010    source источник
comment
Вы можете получить здесь некоторые укусы, если опубликуете код, который фактически извлекает / декодирует / кэширует / истекает срок действия растровых изображений. Проблема почти наверняка в том, что вы там делаете, а не в том, что требует копаться во внутренностях распределения кучи.   -  person Yoni Samlan    schedule 14.06.2010
comment
При отладке кода моим главным подозреваемым всегда является мой код. Чтобы отладить это, я получаю подсказки из окружения. К сожалению, в этом случае: A. создание простого кода, который считывает 3 изображения 600x800 за раз в кеш, время от времени дает аналогичные результаты (проверено. Чтобы это происходило быстрее, можно добавить фиктивные массивы без ссылок), B. Я использую инструменты, чтобы исследовать проблему, но подсказки, которые я получаю от инструментов, не совпадают. C. Суть этого вопроса - получить подсказки о том, что является хорошей практикой, что не так с моими выводами о считывании DDMS, и есть ли обходной путь.   -  person Meymann    schedule 15.06.2010
comment
Очень простой способ воспроизвести проблему в другой форме 1. в вашем классе Activity добавьте static {byte dummy [] = new byte [4096]; }, чтобы заставить кучу расшириться (и устранить сомнения). 2. Создайте ViewFlipper. 3. Добавьте около 10 ImageView, каждый из которых относится к растровому изображению размером 600x800. 4. Когда вылетает, посмотрите DDMS.   -  person Meymann    schedule 21.06.2010


Ответы (5)


Думаю, в вашем случае нет ничего особенного. Просто не хватает памяти. У вас не может быть в памяти несколько растровых изображений 600x800, они занимают слишком много памяти. Вы должны сохранить их на SD и загрузить в память по запросу. Я думаю, это именно то, что вы делаете.

Вы должны знать одну вещь: DDMS отображает потребление памяти кучи java. Но есть еще и собственная память, которая не отображается в DDMS. И растровые изображения, насколько я понимаю, создаются в родной памяти. Так что DDMS - просто плохой инструмент для отслеживания этих проблем с памятью. Вам просто нужно убедиться, что вы освободили свою память, что изображения собираются сборщиком мусора после того, как они вам больше не нужны.

Сборщик мусора работает по собственному графику. Вот почему вы должны вызывать метод Bitmap.recycle () для растровых изображений, которые вам больше не нужны. Этот метод освобождает именно ту внутреннюю память, которая у вас закончилась. Таким образом, вы не зависите от GC и сможете как можно скорее освободить самый большой кусок памяти.

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

Вот хорошая запись о распределении памяти, это может помочь вам копнуть глубже

person Fedor    schedule 06.07.2010
comment
Конечно, я делал ленивую загрузку с SD. Это то, что делает Галерея. Однако я использую кеш-память на 4 слота, чтобы ускорить его. Тем не менее, мне нужно понять три вещи: 1. Как заставить Gallery отправлять неиспользуемые изображения как можно скорее? 2. Почему этого недостаточно? 800x600x4imagesx3bytesPerPixel = 5M, что меньше 16M. 3. Интересно, почему это так нерегулярно. Иногда срабатывает, и все время летает в разных местах. Спасибо - person Meymann; 07.07.2010
comment
Поскольку вы используете кеш с 4 слотами, вы можете вызвать recycle () при удалении старого изображения из этого кеша. - person Fedor; 07.07.2010
comment
Когда getView вызывается в адаптере, вам передается convertView. На самом деле это мнение перерабатывается. Экземпляр растрового изображения, отображаемый в этом представлении, больше не нужен. Итак, я думаю, вы можете отобразить растровое изображение в этом ImageView и переработать его. Это что-то близкое к подходу как можно скорее. - person Fedor; 07.07.2010
comment
Я боюсь, что даже без кеша изображения, привязанные к галерее, не освобождаются, как только они покидают экран (они не освобождаются, даже если они не являются соседями отображаемого прямоугольника, в то время как Gallery.getChildCount () говорит 1). Я использую элементы Bitmap Options, такие как isPurgable и isInputShareable. Это намного лучше, но все же недостаточно. Эта команда adb shell dumpsys meminfo ‹process› показывает огромную память, выделенную за моей спиной в родной. Это намного больше, чем действительно нужно системе. Большое спасибо. - person Meymann; 07.07.2010
comment
А, ну, convertView всегда имеет значение null. Я добавил условие if, чтобы уловить его ... и оно никогда не бывает нулевым :( Спасибо. - person Meymann; 07.07.2010
comment
Плохо. Это указывает на то, что в реализации вашего адаптера есть еще несколько проблем, и он не перерабатывает представления. - person Fedor; 07.07.2010
comment
Вы можете посмотреть приложение Фотки в Маркете. Он отображает полноэкранные фотографии. Прекрасно работает и не подводит. Мы используем какой-то нестандартный адаптер, а не галерею, но он очень похож. Мы не кэшируем растровые изображения в памяти только на SD. Когда нам нужно растровое изображение, мы парсим его из SD. Просмотры перерабатываются. - person Fedor; 07.07.2010

Не уверен, что это вариант для вас, но пробовали ли вы суперсэмплинг изображений Странно проблема нехватки памяти при загрузке изображения в объект Bitmap?

person Fedor    schedule 01.07.2010
comment
Спасибо, но боюсь, что не пойдет ... Убивает качество изображения, так как позволяет декодеру быть более корявым. Следовательно, текст и небольшие фрагменты изображения становятся очень размазанными ... Как будто JPEG умеет размазывать ... Это действительно похоже на то, что происходит, когда вы берете эскиз JPEG EXIF ​​и изменяете его размер до полноразмерного. Спасибо - person Meymann; 06.07.2010

Я также столкнулся с подобной проблемой пару недель назад, и я решил ее, уменьшив масштаб изображения до оптимальной точки. Я написал полный подход в своем блоге здесь и загрузил полный образец проекта с кодом, подверженным OOM, и кодом проверки OOM здесь.

person Shailendra Singh Rajawat    schedule 12.06.2014

С тех пор, как я спросил об этом, прошло много времени.

Ответ следует разделить на 2 части: Pre-Gingerbread: вы просто используете маленькие изображения, используете субдискретизацию, возможно, одну фотографию размера экрана и надеетесь на лучшее. Перед получением растрового изображения убедитесь, что вы не выделяете крошечные элементы, которые нельзя освободить. До Ginger память для BMPS должна была быть непрерывной, и она не учитывалась в памяти ВМ. Всегда смотрите на логарифм. Посмотрите лекцию о памяти от Google IO 2011. Опубликовать Джинджер - это проще. Начиная с Honeycomb, растровые изображения учитываются даже в вашей области java. Нет области джни. Всегда используйте переработку для растровых изображений, которые вам не нужны. Не ждите GC.

person Meymann    schedule 13.06.2014

Вопрос был задан в 2010 году, когда Фройо был еще свеж. С тех пор произошло так много всего. До версии 3.0 битовые карты размещались в JNI. Память не отображалась в статистике Dalvik. Он больше не должен быть монолитным. До версии 2.3 статистика памяти для JNI не была доступна (вызовы JNI для декодирования растровых изображений) в logcat. 4.4 эвакуировал больше места. 5.0 большой взрыв искусства. Еще в 2010 году Nexus One был высокого класса с объемом памяти менее 300 МБ. Бюджет приложения составлял около 16 МБ. Сейчас память примерно в 8 раз больше.

person Meymann    schedule 15.05.2015