Ошибка OutOfMemory, хотя имеется свободная память.

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

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 мегабайт). Означает ли это, что куча внутренне фрагментирована и нет смежных сегментов памяти, достаточно больших для обработки нашего распределения? Если это так, что мне делать? Если это не так, то что?

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

Solutions Collecting From Web of "Ошибка OutOfMemory, хотя имеется свободная память."

Странное поведение заключается в том, что растровые изображения выделяются на нативной куче, а не на собранном мусоре, но андроид может отслеживать объекты только в собранной кучей мусора. Из 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(); } 

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

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

Растровые изображения потребляют нативную кучу, которая обычно не отображается в 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()); 

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

  • Растровые изображения на Android
  • Android: Eclipse MAT не показывает все объекты моего приложения

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

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

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

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

 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(); в 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();