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

Фрагментация памяти GC GC не выполняется. Обходной путь?

Я тестирую версию android 3.1, большой вариант heapsize, доступно около 250M памяти.

Я устанавливаю следующий код для запуска, когда я нажимаю кнопку "Тест" в своих приложениях:

float [][][]foo = new float[3][2048][2048];
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888);
bm.recycle();
bm  = null;
foo = null;

У меня много памяти для этого - я могу нажать кнопку несколько раз без проблем.

Но если я продолжаю нажимать кнопку, в конечном итоге (менее 20 ударов), она умирает с OutOfMemory. [Обычно в android.graphics.Bitmap.nativeCreate(собственный метод)]

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

Это из-за фрагментации или просто ужасная ошибка в коде Bitmap android и/или GC? Или я просто делаю что-то глупое? (Пожалуйста, пусть это будет что-то глупое...)

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

[Обновление]

Я подтвердил, что это проблема фрагментации или ошибка gc, так как дамп кучи показывает, что я использую только 5.6M, когда простаивание (без утечек) достигает максимума около 26M во время обработки. (Кроме того, нативная куча остается ниже 4M.) Пока куча java тем временем растет в объеме вплоть до предела 280M на моем тестовом устройстве, в котором я начинаю получать исключения OutOfMemory. Поэтому я использую только 10% моей доступной кучи на пике, но получаю OutOfMemory.

[Добавление вызова к System.gc(), к сожалению, устраняет простой тестовый пример, который я привел выше. Я говорю неудачно, потому что (A) это не должно меняться, и (B), потому что я уже регулярно нахожу его в своем реальном коде, поэтому это означает, что мой простой пример теста слишком прост.]

Кто-нибудь еще сталкивается с этим? Любые обходные пути? Есть ли законченный способ перезапустить мое приложение?

[Обновление]

Следующая версия надежно вызывает OutOfMemory в 3-4 вызовах (нажатия кнопки):

float [][][]foo = new float[3][2048][2048];
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888);
int []bar = new int[3*2048*2048];
bm.recycle();
bm = null;
System.gc();
foo = null;
System.gc();
bar = null;
System.gc();

Трассировка памяти показывает, что куча неуклонно растет каждый вызов, пока не достигнет предела и не умрет. Если я удалю любое из трех распределений, оно достигнет равновесия и сохранится бесконечно. Удаление всех, кроме последнего gc(), заставляет его умереть чуть раньше.

Я бы сказал, что это проблема фрагментации, а не ошибка gc как таковая. Если кто-нибудь знает, как это исправить, дайте мне знать. Распределение int [] предназначено для записи растрового изображения, поэтому у меня нет возможности выделить его как 2d-массив (ограничение библиотеки битовых карт Android).

4b9b3361

Ответ 1

Вот еще одна страница SO, которая, по-видимому, имеет обходной путь для этой проблемы:

Странная проблема с памятью при загрузке изображения в объект Bitmap

В частности, ответ Эфраим (выдержка):

"1) Каждый раз, когда вы делаете BitmapFactory.decodeXYZ(), обязательно передавайте в BitmapFactory.Options с inPurgeable, установленным в true (и предпочтительно с inInputShareable также установлено значение true).

"2) НИКОГДА не используйте Bitmap.createBitmap(ширина, высота, Config.ARGB_8888). Я имею в виду НИКОГДА! У меня никогда не было этой вещи, которая не вызывала ошибку памяти после нескольких проходов. Никакого количества recycle(), System.gc(), что бы ни помогало, всегда возникало исключение. Другой способ, который на самом деле работает, - иметь фиктивный образ в ваших чертежах (или другом растровом изображении, который вы декодировали с помощью шага 1 выше), масштабировать его до того, что вы хотите, а затем манипулировать (например, передавать его на холст для большего удовольствия).Так что вы должны использовать вместо этого: Bitmap.createScaledBitmap(srcBitmap, width, height, false). Если по какой-либо причине вы ДОЛЖНЫ использовать метод создания грубой силы, то, по крайней мере, передайте Config.ARGB_4444."

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

Я бы добавил, что Diane Hackborn прокомментировала, что с 3.0 Android больше не выделяет растровые изображения из родной кучи, а вместо этого напрямую выделяет их из обычной кучи. Это может привести к тому, что ваши родные фигуры кучи не имеют значения. См. Комментарий hackbod на этой странице:

Растровые изображения в Android

Я предполагаю, что это подразумевает довольно существенное изменение в отношении Honeycomb относительно распределения битмапа и поэтому может объяснить, почему есть ошибки с такими выделениями (если есть). Я не знаю, какое влияние это изменение оказывает на команду recycle(), но в свете вышеприведенных комментариев Эфраим ответ может быть "не очень хорошим".

Наконец,

Чтобы использовать largeHeap для глотания огромных растровых изображений, можно увидеть, что вы не играете хорошо с другими приложениями, особенно если вы приближаетесь к физическим пределам устройства. Я не уверен, как вы можете этого избежать, но будьте готовы к активному действию onPause()/onResume(), так как ваши действия приложений в других приложениях, и они отступают от ваших. Этот ответ SO включает в себя обсуждение этого вопроса:

Определить размер кучи приложений в Android

Ответ 2

Чтобы предотвратить фрагментацию, вы можете просто выделить большой массив AND Bitmap один раз и повторно использовать его.

Для Android есть некоторые предостережения, так как Android пытается в какой-то степени управлять вашими приложениями. Например, Activity или View могут быть выгружены, если они не видны, и повторно запустите позже, если они снова станут видимыми. Поэтому большие вещи лучше хранить с помощью объекта Application или места static.

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

Ответ 3

Существует очень подробная статья об управлении растровой памятью на android developers.android.com:

http://developer.android.com/training/displaying-bitmaps/manage-memory.html

В основном они рекомендуют использовать Bitmap.recycle(), чтобы избежать ошибок OutOfMemoryError для Android 2.3.3 и ниже. Согласно документации, он освобождает собственный объект, связанный с объектом Bitmap.