我目前正在努力应对Android平台的一种奇怪行为-位图/Java堆内存限制。根据设备的不同,Android会将应用程序开发人员的Java堆空间限制为16 MiB,24 MiB或32 MiB(或者您可能会在有根电话上找到任何随机值)。可以说它很小,但是相对简单,因为我可以使用以下API来衡量使用情况:

Runtime rt = Runtime.getRuntime();
long javaBytes = rt.totalMemory() - rt.freeMemory();
long javaLimit = rt.maxMemory();

足够容易;现在为扭曲。在Android中,除少数异常(exception),位图存储在 native 堆中,并且不计入Java堆。 Google的一些眼光敏锐,纯粹的开发人员认为这很“糟糕”,并允许开发人员获得“超出其应有份额的 yield ”。因此,有一段不错的代码片断可以计算出位图以及可能的其他资源所引起的 native 内存使用情况,并将其与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(捕获到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(这是所有加载的位图中的4 * #pixels)
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):回收位图'game_word_puzzle_11_aniframe_005'
I/Epic(23221):BITMAP_RECYCLING:回收了1张值(value)110万的位图)。年龄= 294

请注意totalMemory-freeMemory仅是4.7 MiB,却是〜26?位图占用的 native 内存的MiB,我们在达到极限的31/32 MiB范围内。我在这里仍然有些困惑,因为我所有加载的位图的运行总计是26.6 MiB,但是 native 分配大小仅为25.2 MiB。所以我算错了。但这一切都在球场上,并明确地说明了内存限制发生的跨池“求和”。

我想我已经把它修好了。但是不,Android不会轻易放弃...

这是我从四种测试设备中的两种获得的结果:

I/dalvikvm-heap(17641):将目标GC堆从32.687MB限制为32.000MB
D/dalvikvm(17641):GC_FOR_MALLOC释放D/dalvikvm(17641):GC_EXTERNAL_ALLOC释放E/dalvikvm-heap(17641):1111200字节的外部分配对于此进程而言太大。
E/dalvikvm(17641):内存不足:堆大小= 7815KB,已分配= 4684KB,位图大小= 24443KB,限制= 32768KB
E/dalvikvm(17641):修剪信息:占地面积= 7815KB,允许的占地面积= 7815KB,修剪= 880KB
E/GraphicsJNI(17641):VM不会让我们分配1111200字节
I/dalvikvm-heap(17641):将目标GC堆从32.686MB限制为32.000MB
D/dalvikvm(17641):GC_FOR_MALLOC释放I/DEBUG(1505):*** *** *** *** *** *** *** *** *** *** *** *** *** *** * ** ***
I/DEBUG(1505):构建指纹:“verizon_wwe/htc_mecha/mecha:2.3.4/GRJ22/98797:user/release-keys”
I/DEBUG(1505):PID:17641,TID:17641
I/DEBUG(1505):信号11(SIGSEGV),代码1(SEGV_MAPERR),故障加法器00000000
I/调试(1505):r0 0055dab8 r1 00000000 r2 00000000 r3 0055dadc
I/调试(1505):r4 0055dab8 r5 00000000 r6 00000000 r7 00000000
I/调试(1505):r8 000002b7 r9 00000000 10 00000000 fp 00000384
I/DEBUG(1505):ip 0055dab8 sp befdb0c0 lr 00000000 pc ab14f11c cpsr 60000010
I/调试(1505):d0 414000003f800000 d1 2073646565637834
I/调试(1505):d2 4de4b8bc426fb934 d3 42c80000007a1f34
I/调试(1505):d4 00000008004930e0 d5 0000000000000000
I/调试(1505):d6 0000000000000000 d7 4080000080000000
I/调试(1505):d8 0000025843e7c000 d9 c0c0000040c00000
I/调试(1505):d10 40c0000040c00000 d11 0000000000000000
I/调试(1505):d12 0000000000000000 d13 0000000000000000
I/调试(1505):d14 0000000000000000 d15 0000000000000000
I/DEBUG(1505):d16 afd4242840704ab8 d17 0000000000000000
I/调试(1505):d18 0000000000000000 d19 0000000000000000
I/调试(1505):d20 0000000000000000 d21 0000000000000000
I/调试(1505):d22 0000000000000000 d23 0000000000000000
I/调试(1505):d24 0000000000000000 d25 0000000000000000
I/调试(1505):d26 0000000000000000 d27 0000000000000000
I/调试(1505):d28 00ff00ff00ff00ff d29 00ff00ff00ff00ff
I/调试(1505):d30 0000000000000000 d31 3fe55167807de022
I/DEBUG(1505):SCR 68000012

那是 native 崩溃。分段故障同样如此(sig11)。根据定义,段错误始终是错误。这绝对是处理GC和/或内存限制检查的 native 代码中的Android错误。但是仍然是我的应用崩溃,导致糟糕的评论,退货和销售下降。

因此,我必须自己计算极限。除了我在这里苦苦挣扎。我尝试自己添加像素(EpicPixels),但仍然会定期碰到memcrash,因此我低估了一些内容。我尝试将javaBytes(总计-免费)添加到NativeHeapAllocSize,但这有时会导致我的应用程序变成“厌食症”,释放和释放位图,直到没有要清除的东西为止。
  • 是否有人知道用于计算内存限制并触发java.lang.OutOfMemory的确切计算?
  • 还有其他人遇到这个问题并解决了吗?你有智慧的明珠吗?
  • 有谁知道哪位Google员工梦this以求,因此我可以打他毁掉40个小时的生命? j/k

  • 解答:此限制适用于NativeHeapAllocSize

    最佳答案

    使用此片段,对我有用

    /**
     * Checks if a bitmap with the specified size fits in memory
     * @param bmpwidth Bitmap width
     * @param bmpheight Bitmap height
     * @param bmpdensity Bitmap bpp (use 2 as default)
     * @return true if the bitmap fits in memory false otherwise
     */
    public static boolean checkBitmapFitsInMemory(long bmpwidth,long bmpheight, int bmpdensity ){
        long reqsize=bmpwidth*bmpheight*bmpdensity;
        long allocNativeHeap = Debug.getNativeHeapAllocatedSize();
    
    
        final long heapPad=(long) Math.max(4*1024*1024,Runtime.getRuntime().maxMemory()*0.1);
        if ((reqsize + allocNativeHeap + heapPad) >= Runtime.getRuntime().maxMemory())
        {
            return false;
        }
        return true;
    
    }
    

    这是一个使用示例
            BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
            bmpFactoryOptions.inJustDecodeBounds=true;
            BitmapFactory.decodeFile(path,bmpFactoryOptions);
            if ( (runInSafeMemoryMode()) && (!Manager.checkBitmapFitsInMemory(bmpFactoryOptions.outWidth, bmpFactoryOptions.outHeight, 2)) ){
                Log.w(TAG,"Aborting bitmap load for avoiding memory crash");
                return null;
            }
    

    关于android - Android位图限制-防止java.lang.OutOfMemory,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6892676/

    10-11 04:56