Подтвердить что ты не робот

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

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


Среда

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

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

Чтобы "ускорить" пользовательский опыт, у меня есть простой кэш из 4 слотов, которые предварительно заполняют (в петлере) примерно 1 изображение слева и 1 изображение прямо на отображаемое изображение и сохраняют их в 4-х слот-хэш-карте.

Платформа

Я использую AVD 256 RAM и 128 Heap Size, с экраном 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-считывание неправильно, и на самом деле нет блока 900K для выделения? Если да, то кто-нибудь может сказать мне, где я могу это видеть?

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

Meymann

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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