Android - восстановление из-за нехватки памяти - PullRequest
10 голосов
/ 06 ноября 2011

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

Тем не менее, когда я использую приложение в определенный момент, я начинаю видеть сообщения в LogCat, как показано ниже

11-05 15:57:43.951: I/dalvikvm-heap(5449): Clamp target GC heap from 32.655MB to 32.000MB
11-05 15:57:43.951: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 24% free 11512K/14983K, external 17446K/17896K, paused 64ms
11-05 15:57:44.041: D/dalvikvm(5449): GC_EXTERNAL_ALLOC freed <1K, 24% free 11511K/14983K, external 17258K/17896K, paused 77ms

Если я продолжу, сообщения выше станут более срочными

11-05 16:02:09.590: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 23% free 11872K/15239K, external 17497K/17896K, paused 71ms
11-05 16:02:09.700: D/dalvikvm(5449): GC_EXTERNAL_ALLOC freed <1K, 23% free 11872K/15239K, external 17497K/17896K, paused 84ms
11-05 16:02:09.720: E/dalvikvm-heap(5449): 192816-byte external allocation too large for this process.
11-05 16:02:09.800: I/dalvikvm-heap(5449): Clamp target GC heap from 33.057MB to 32.000MB
11-05 16:02:09.800: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 23% free 11872K/15239K, external 17497K/17896K, paused 68ms
11-05 16:02:09.800: E/GraphicsJNI(5449): VM won't let us allocate 192816 bytes

И приложение неизбежно завершится с OutOfMemoryException Симптомами являются классическая утечка памяти. Тем не менее, в моем методе Fragment#onDestroy я отменяю все отложенные задачи, отменяю привязку и аннулирую представления и вызываю Bitmap # recycle. Интересно, что я вижу вызовы GC в LogCat, но даже если я задерживаюсь на длительный период, память никогда не освобождается.

У меня такое ощущение, что постоянное перечитывание изображений с SD вызывает деградацию и неизбежный упадок

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

public static void unbindDrawables(View view, final boolean agressive) {
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
                unbindDrawables(((ViewGroup) view).getChildAt(i), agressive);
            }
            if (!AdapterView.class.isInstance(view)) {
                ((ViewGroup) view).removeAllViews();
            }
        } else {
            Drawable bmp = view.getBackground();
            if (bmp == null && ImageView.class.isInstance(view)) bmp = ((ImageView) view).getDrawable();
            if (bmp != null) {
                bmp.setCallback(null);
                if (agressive && (TaggedDrawable.class.isInstance(bmp))) {
                    Bitmap bm = ((BitmapDrawable) bmp).getBitmap();
                    if (bm != null) {
                        if (DEBUG) Log.i(TAG, "Forcing bitmap recycle for " + bmp);
                        bm.recycle();
                        bm = null;
                        view.destroyDrawingCache();
                        view = null;
                    }
                }
            }
        }
    }

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

Ответы [ 3 ]

10 голосов
/ 06 декабря 2011

Во-первых, GC не будет перезаписывать память сразу же после того, как вы позвонили. Вот несколько советов от разработчика сайтов Android:

  1. Не хранить долгоживущие ссылки на контекстную деятельность (ссылка к деятельности должен иметь тот же жизненный цикл, что и деятельность сам)

  2. Попробуйте использовать контекстное приложение вместо контекстной активности

  3. Избегайте нестатических внутренних классов в деятельности, если вы не контролируете их жизненный цикл, использовать статический внутренний класс и сделать слабую ссылку к активности внутри. Решением этой проблемы является использование статический внутренний класс с WeakReference на внешний класс, как это было сделано во ViewRoot и его внутреннем классе W, например

  4. Сборщик мусора не является страховкой от утечек памяти

Во-вторых, попробуйте использовать BitmapFactory.options, если вам не очень важно качество растрового изображения.

В-третьих, используйте try catch для обработки исключения OutOfMemory в блоке catch.

Наконец, используйте Анализатор памяти . Откройте DDMS в Eclipse, на панели инструментов есть кнопка обновления кучи. Вы можете использовать это для создания файла hprof, а затем с помощью инструмента hprof-conv в каталоге andorid-sdk-tools преобразовать файл в файл указанного формата, который может прочитать Memory Analyzer. Теперь вы можете использовать анализатор памяти для анализа возможной утечки памяти. Это действительно хороший инструмент, который даст вам много советов, чтобы избежать запоминания.

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

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

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

Также см. Это Тема

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

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

Попробуйте, если высота и ширина зависят от вас, в настоящее время m использует 1024 для обоих.

public class BitmapUtils {

public static Bitmap resizeBitmap( Bitmap input, int destWidth, int destHeight )
{
    int srcWidth = input.getWidth();
    int srcHeight = input.getHeight();
    boolean needsResize = false;
    float p;
    if ( srcWidth > destWidth || srcHeight > destHeight ) {
        needsResize = true;
        if ( srcWidth > srcHeight && srcWidth > destWidth ) {
            p = (float)destWidth / (float)srcWidth;
            destHeight = (int)( srcHeight * p );
        } else {
            p = (float)destHeight / (float)srcHeight;
            destWidth = (int)( srcWidth * p );
        }
    } else {
        destWidth = srcWidth;
        destHeight = srcHeight;
    }
    if ( needsResize ) {
        Bitmap output = Bitmap.createScaledBitmap( input, destWidth, destHeight, true );
        return output;
    } else {
        return input;
    }
 }
}
...