В настоящее время я борюсь со странным поведением платформы Android - ограничением памяти кучи Bitmap / Java. В зависимости от устройства Android ограничивает разработчика приложения 16, 24 или 32 МБ пространства кучи Java (или вы можете найти любое случайное значение на рутированном телефоне). Это, возможно, довольно мало, но относительно просто, поскольку я могу измерить использование с помощью следующих API:
Runtime rt = Runtime.getRuntime();
long javaBytes = rt.totalMemory() - rt.freeMemory();
long javaLimit = rt.maxMemory();
Достаточно просто; теперь поворот. В Android растровые изображения, за некоторыми исключениями, хранятся в собственной куче и не учитываются в куче Java. Какой-то проницательный и пуристский разработчик в Google решил, что это «плохо», и позволил разработчику получить «больше, чем полагается». Итак, есть этот красивый небольшой фрагмент кода, который вычисляет использование собственной памяти, вызванное растровыми изображениями и, возможно, другими ресурсами, и суммирует это с кучей Java, и если вы перейдете ... java.lang.OutOfMemory. ой
Но ничего страшного. У меня много растровых изображений, и я не нуждаюсь в них все время. Я могу "вывести" на страницу некоторые из тех, которые не используются в данный момент:
Итак, для попытки №1 я реорганизовал код, чтобы я мог обернуть каждую загрузку растрового изображения с помощью try / catch:
while(true) {
try {
return BitmapFactory.decodeResource(context.getResources(), android_id, bitmapFactoryOptions);
} catch (java.lang.OutOfMemory e) {
// Do some logging
// Now free some space (the code below is a simplified version of the real thing)
Bitmap victim = selectVictim();
victim.recycle();
System.gc(); // REQUIRED; else, weird behavior ensues
}
}
Смотрите, вот небольшой отрывок из журнала, показывающий, что мой код перехватывает исключение и повторно использует растровые изображения:
E/Epic (23221): OUT_OF_MEMORY (caught java.lang.OutOfMemory) I/Epic (23221): ArchPlatform[android].logStats() - I/Epic (23221): LoadedClassCount=0.00M I/Epic (23221): GlobalAllocSize=0.00M I/Epic (23221): GlobalFreedSize=0.02M I/Epic (23221): GlobalExternalAllocSize=0.00M I/Epic (23221): GlobalExternalFreedSize=0.00M I/Epic (23221): EpicPixels=26.6M (this is 4 * #pixels in all loaded bitmaps) I/Epic (23221): NativeHeapSize=29.4M I/Epic (23221): NativeHeapAllocSize=25.2M I/Epic (23221): ThreadAllocSize=0.00M I/Epic (23221): totalMemory()=9.1M I/Epic (23221): maxMemory()=32.0M I/Epic (23221): freeMemory()=4.4M W/Epic (23221): Recycling bitmap 'game_word_puzzle_11_aniframe_005' I/Epic (23221): BITMAP_RECYCLING: recycled 1 bitmaps worth 1.1M). age=294
Обратите внимание, что totalMemory - freeMemory занимает всего 4,7 МБ, но с ~ 26? МиБ собственной памяти, занятой растровыми изображениями, мы находимся в диапазоне 31/32 МБ, где мы достигли предела. Я все еще немного сбит с толку, так как мой текущий счет всех загруженных растровых изображений составляет 26,6 МБ, но собственный размер выделения составляет всего 25,2 МБ. Значит я что-то не так считаю. Но это все приблизительно и однозначно демонстрирует "суммирование" кросс-пула, происходящее с пределом памяти.
Я ДУМАЛ, что исправил это. Но нет, Android так легко не сдался бы ...
Вот что я получил на двух из четырех моих тестовых устройств:
I/dalvikvm-heap(17641): Clamp target GC heap from 32.687MB to 32.000MB D/dalvikvm(17641): GC_FOR_MALLOC freed <1K, 41% free 4684K/7815K, external 24443K/24443K, paused 24ms D/dalvikvm(17641): GC_EXTERNAL_ALLOC freed <1K, 41% free 4684K/7815K, external 24443K/24443K, paused 29ms E/dalvikvm-heap(17641): 1111200-byte external allocation too large for this process. E/dalvikvm(17641): Out of memory: Heap Size=7815KB, Allocated=4684KB, Bitmap Size=24443KB, Limit=32768KB E/dalvikvm(17641): Trim info: Footprint=7815KB, Allowed Footprint=7815KB, Trimmed=880KB E/GraphicsJNI(17641): VM won't let us allocate 1111200 bytes I/dalvikvm-heap(17641): Clamp target GC heap from 32.686MB to 32.000MB D/dalvikvm(17641): GC_FOR_MALLOC freed <1K, 41% free 4684K/7815K, external 24443K/24443K, paused 17ms I/DEBUG ( 1505): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** I/DEBUG ( 1505): Build fingerprint: 'verizon_wwe/htc_mecha/mecha:2.3.4/GRJ22/98797:user/release-keys' I/DEBUG ( 1505): pid: 17641, tid: 17641 I/DEBUG ( 1505): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000 I/DEBUG ( 1505): r0 0055dab8 r1 00000000 r2 00000000 r3 0055dadc I/DEBUG ( 1505): r4 0055dab8 r5 00000000 r6 00000000 r7 00000000 I/DEBUG ( 1505): r8 000002b7 r9 00000000 10 00000000 fp 00000384 I/DEBUG ( 1505): ip 0055dab8 sp befdb0c0 lr 00000000 pc ab14f11c cpsr 60000010 I/DEBUG ( 1505): d0 414000003f800000 d1 2073646565637834 I/DEBUG ( 1505): d2 4de4b8bc426fb934 d3 42c80000007a1f34 I/DEBUG ( 1505): d4 00000008004930e0 d5 0000000000000000 I/DEBUG ( 1505): d6 0000000000000000 d7 4080000080000000 I/DEBUG ( 1505): d8 0000025843e7c000 d9 c0c0000040c00000 I/DEBUG ( 1505): d10 40c0000040c00000 d11 0000000000000000 I/DEBUG ( 1505): d12 0000000000000000 d13 0000000000000000 I/DEBUG ( 1505): d14 0000000000000000 d15 0000000000000000 I/DEBUG ( 1505): d16 afd4242840704ab8 d17 0000000000000000 I/DEBUG ( 1505): d18 0000000000000000 d19 0000000000000000 I/DEBUG ( 1505): d20 0000000000000000 d21 0000000000000000 I/DEBUG ( 1505): d22 0000000000000000 d23 0000000000000000 I/DEBUG ( 1505): d24 0000000000000000 d25 0000000000000000 I/DEBUG ( 1505): d26 0000000000000000 d27 0000000000000000 I/DEBUG ( 1505): d28 00ff00ff00ff00ff d29 00ff00ff00ff00ff I/DEBUG ( 1505): d30 0000000000000000 d31 3fe55167807de022 I/DEBUG ( 1505): scr 68000012
Это родной сбой. Не менее segfault (sig11). По определению segfault ВСЕГДА является ошибкой. Это абсолютно ошибка Android в собственном коде, обрабатывающем сборщик мусора и / или проверке ограничения памяти. Но мое приложение по-прежнему дает сбой, что приводит к плохим отзывам, возвратам и снижению продаж.
Итак, я должен вычислить предел сам. За исключением того, что я боролся здесь. Я сам пробовал складывать пиксели (EpicPixels), но все равно периодически вылетаю из памяти, поэтому я что-то занижаю. Я попытался добавить javaBytes (полностью - бесплатно) в NativeHeapAllocSize, но иногда это приводило к тому, что мое приложение становилось «анорексичным», освобождая и освобождая растровые изображения до тех пор, пока нечего было очищать.
Кто-нибудь знает точную вычисление, используемое для вычисления предела памяти и запуска java.lang.OutOfMemory?
Кто-нибудь еще сталкивался с этой проблемой и работал над ней? Есть ли у вас какие-нибудь жемчужины мудрости?
Кто-нибудь знает, какой сотрудник Google придумал эту схему, чтобы я мог ударить его за то, что он испортил ~ 40 часов моей жизни? j / k
ОТВЕТ: Предел указан для NativeHeapAllocSize