Ошибка OutOfMemory, хотя свободная память доступна - PullRequest
13 голосов
/ 15 октября 2011

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

    10-14 13:43:53.020: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 40.637MB for 942134-byte allocation
    10-14 13:43:53.070: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 126K, 11% free 41399K/46343K, paused 31ms
    10-14 13:43:53.130: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 920K, 13% free 40478K/46343K, paused 30ms
    10-14 13:43:53.180: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1026K, 13% free 40479K/46343K, paused 30ms
    10-14 13:43:53.250: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 931K, 12% free 41193K/46343K, paused 31ms
    10-14 13:43:53.250: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 41.313MB for 1048592-byte allocation
    10-14 13:43:53.280: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed <1K, 11% free 42217K/47431K, paused 31ms
    10-14 13:44:01.520: DEBUG/dalvikvm(31533): GC_CONCURRENT freed 3493K, 15% free 40646K/47431K, paused 3ms+9ms
    10-14 13:44:08.130: DEBUG/dalvikvm(31533): GC_EXPLICIT freed 16461K, 47% free 25527K/47431K, paused 3ms+6ms
    10-14 13:44:09.150: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1007K, 45% free 26191K/47431K, paused 35ms
    10-14 13:44:09.160: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 29.334MB for 3850256-byte allocation
    10-14 13:44:09.200: DEBUG/dalvikvm(31533): GC_CONCURRENT freed 0K, 37% free 29951K/47431K, paused 2ms+4ms
    10-14 13:44:11.970: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1878K, 38% free 29784K/47431K, paused 37ms
    10-14 13:44:12.410: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 62K, 36% free 30441K/47431K, paused 32ms
    10-14 13:44:12.440: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed <1K, 32% free 32325K/47431K, paused 32ms
    10-14 13:44:12.440: INFO/dalvikvm-heap(31533): Forcing collection of SoftReferences for 3850256-byte allocation
    10-14 13:44:12.480: DEBUG/dalvikvm(31533): GC_BEFORE_OOM freed 124K, 33% free 32200K/47431K, paused 37ms
    10-14 13:44:12.480: ERROR/dalvikvm-heap(31533): Out of memory on a 3850256-byte allocation.

Я прошу прощения за включение такого большого количества журналов, я надеюсь, что это актуально.Насколько я понимаю, система непрерывно перенастраивает размер кучи до тех пор, пока она не достигнет максимума кучи.Затем мы запрашиваем особо большое выделение, которое не выполняется.Очевидно, что доступной памяти более чем достаточно (около 15 мегабайт).Означает ли это, что куча внутренне фрагментирована и нет непрерывных сегментов памяти, достаточно больших, чтобы справиться с нашим распределением?Если это так, что мне делать?Если это не так, то что?

Заранее спасибо.

Ответы [ 4 ]

16 голосов
/ 16 октября 2011

Странное поведение в том, что растровые изображения размещаются в собственной куче, а не в сборщике мусора, но android может только отслеживать объекты в куче, собираемой мусором.В Android 2.2 (или, возможно, 2.3) это изменилось, и выделенные растровые изображения также видны, если вы создаете дамп кучи.

Возвращаясь к вопросу, скорее всего, ваша проблема в том, что загруженные вручную растровые изображения не освобождаются надлежащим образом,Одна типичная проблема заключается в том, что некоторый обратный вызов остается установленным, или представление все еще ссылается на растровое изображение.Другая распространенная проблема заключается в том, что если вы загружаете большие растровые изображения вручную (не как ресурс), вам нужно будет вызывать recycle () для них, когда они вам больше не нужны, что освободит растровое изображение из собственной памяти и избавит от мусора.Коллекционер сможет работать так, как должен.(GC видит только объекты в куче GC и не определяет, какой объект освободить для освобождения памяти из собственной кучи, и на самом деле даже не заботится об этом.)

У меня есть этот маленький ребеноквсе время под рукой:

public static void stripImageView(ImageView view) {
    if ( view.getDrawable() instanceof BitmapDrawable ) {
        ((BitmapDrawable)view.getDrawable()).getBitmap().recycle();
    }
    view.getDrawable().setCallback(null);
    view.setImageDrawable(null);
    view.getResources().flushLayoutCache();
    view.destroyDrawingCache();
}
2 голосов
/ 05 ноября 2011

Изображения извлекаются из Интернета, каждый размером от 300K до 500K, и сохраняются в arrayList of Drawables.

Размер файла изображения в килобайтахзагрузка из интернета напрямую не актуальна.Поскольку они преобразуются в растровые изображения, вам нужно рассчитать ширину * высоту * 4 байта на изображение для обычных изображений ARGB.(ширина и высота в пикселях).

Растровые изображения используют собственную кучу, которая обычно не отображается в hprof.Hprof должен показывать только количество оставшихся объектов, то есть BitmapDrawables или Bitmaps.

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

public static void logHeap(Class clazz) {
    Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576));
    Double available = new Double(Debug.getNativeHeapSize())/1048576.0);
    Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0);
    DecimalFormat df = new DecimalFormat();
    df.setMaximumFractionDigits(2);
    df.setMinimumFractionDigits(2);

    Log.d(APP, "debug. =================================");
    Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.android.","") + "]");
    Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)");
    System.gc();
    System.gc();

    // don't need to add the following lines, it's just an app specific handling in my app        
    if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) {
        android.os.Process.killProcess(android.os.Process.myPid());
    }
}

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

logHeap(this.getClass());

Вот несколько информативных ссылок - как правило, здесь много тем по этой теме.

Вот также полезныйслайд от Romain Guy (инженер Android Framework) о мягких ссылках, слабых ссылках, простых кэшах, обработке изображений: http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

0 голосов
/ 07 ноября 2011

Система не перенастраивает размеры кучи.Ваше приложение имеет (полу) предопределенное пространство кучи.Размеры кучи могут быть скорректированы в некоторых пользовательских ПЗУ.Однако это не совсем относится к вашей проблеме.

Порядок сборки мусора, который вы видите, предполагает, что во время GC_BEFORE_OOM в вашем приложении заканчивается пространство кучи.Статистика, сообщаемая регистратором Dalvik, может быть общесистемной.

0 голосов
/ 07 ноября 2011

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

BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream fis = new FileInputStream(files.getAbsolutePath());
BitmapFactory.decodeStream(fis, null, o);
fis.close();
final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true){
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
BitmapFactory.Options op = new BitmapFactory.Options();
op.inSampleSize = scale;
fis = new FileInputStream(files.getAbsolutePath());
bitmap[i] = BitmapFactory.decodeStream(fis, null, op);
fis.close();
...