Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android位图限制-阻止java.lang.OutOfMemory_Android_Out Of Memory - Fatal编程技术网

Android位图限制-阻止java.lang.OutOfMemory

Android位图限制-阻止java.lang.OutOfMemory,android,out-of-memory,Android,Out Of Memory,我目前正在与Android平台的一个奇怪行为作斗争——位图/Java堆内存限制。根据设备的不同,Android将应用程序开发人员的Java堆空间限制为16、24或32MIB(或者你可以在根手机上找到任意值)。这可以说是相当小,但相对简单,因为我可以使用以下API衡量使用情况: Runtime rt = Runtime.getRuntime(); long javaBytes = rt.totalMemory() - rt.freeMemory(); long javaLimit = rt.max

我目前正在与Android平台的一个奇怪行为作斗争——位图/Java堆内存限制。根据设备的不同,Android将应用程序开发人员的Java堆空间限制为16、24或32MIB(或者你可以在根手机上找到任意值)。这可以说是相当小,但相对简单,因为我可以使用以下API衡量使用情况:

Runtime rt = Runtime.getRuntime();
long javaBytes = rt.totalMemory() - rt.freeMemory();
long javaLimit = rt.maxMemory();
足够容易;现在开始扭转。在Android中,位图除了少数例外都存储在本机堆中,不计入Java堆。谷歌的一些目光敏锐、纯粹主义的开发人员认为这是“不好的”,并允许开发人员获得“超出其公平份额”。因此,有一段很好的代码,可以计算位图和其他资源产生的本机内存使用量,并将其与Java堆相加,如果你看一下。。。。。java.lang.OutOfMemory。哎哟

但没什么大不了的。我有很多位图,并不总是需要所有的位图。我可以“翻出”一些目前没有使用的:

因此,对于try#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 E/Epic(23221):内存不足(捕获java.lang.OutOfMemory) I/Epic(23221):ArchPlatform[android].logStats()- I/Epic(23221):加载类计数=0.00M I/Epic(23221):全球定位=0.00M I/Epic(23221):全球自由度=0.02米 I/Epic(23221):全球卫星定位=0.00M I/Epic(23221):全球互联网规模=0.00M I/Epic(23221):EpicPixels=26.6M(在所有加载的位图中为4*像素) I/Epic(23221):NativeHeapSize=2940万 I/Epic(23221):NativeHeapAllocSize=25.2M I/Epic(23221):螺纹AllocSize=0.00M I/Epic(23221):totalMemory()=9.1M I/Epic(23221):maxMemory()=32.0M I/Epic(23221):freeMemory()=4.4M W/Epic(23221):回收位图“游戏、文字、拼图、11、aniframe、005” I/Epic(23221):位图(回收:回收1张位图,价值110万美元)。年龄=294 请注意totalMemory-freeMemory如何仅为4.7MIB,但有~26?位图占用的本机内存的MiB,我们在31/32 MiB范围内,达到了极限。我仍然有点困惑,因为我的所有加载位图的运行计数是26.6mib,而本地alloc大小只有25.2mib。所以我算错了什么。但这一切都是大概的,并明确表明了跨池“总和”与mem限制的发生

我以为我把它修好了。但不,安卓不会轻易放弃

以下是我从四台测试设备中的两台获得的信息:

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 I/dalvikvm堆(17641):将目标GC堆从32.687MB钳制到32.000MB
D/dalvikvm(17641):GC_FOR_MALLOC freed每个设备的限制不同(如果要按原样加载位图,请使用第三个链接),或者这里有一些技巧来避免该问题,如:

  • 使用应用程序类的onLowMemory()释放一些内存以避免崩溃
  • 在解码位图之前,请指示所需的位图大小。查看链接以了解更多信息:

此链接用于检查堆

  • 当然,还要释放旧位图的内存
好的,所以我开始怀疑本机模式下的限制是对总java堆大小+本机使用内存实施的

该限制基于NativeHeapAllocSize与maxMemory()。您将在下面看到,当我处于22.0 MiB/24 MiB时,我正在分配~1 MiB。限制是您可以分配的内存量的上限。这让我有一段时间很沮丧。在你达到极限之前,崩溃就发生了。因此,在解决方案中需要一个“memoryPad”值,因为尝试分配23.999 MiB/24 MiB将导致几乎100%的时间崩溃。那么如果限制是24个MiB,你能安全使用多少??不知道。20 MiB似乎起作用了。22 MiB似乎起作用了。再靠近一点我就紧张了。根据malloc内存空间在本机进程中的碎片程度,amount会有所不同。当然,这一切都无法衡量,所以请谨慎行事

07-31 18:37:19.031: WARN/Epic(3118): MEMORY-USED: 27.3M = 4.2M + 23.0M. jf=1.7M, nhs=23.3M, nhf=0.0M 07-31 18:37:19.081: INFO/Epic(3118): ArchPlatform[android].logStats() - 07-31 18:37:19.081: INFO/Epic(3118): LoadedClassCount=0.00M 07-31 18:37:19.081: INFO/Epic(3118): GlobalAllocSize=0.02M 07-31 18:37:19.081: INFO/Epic(3118): GlobalFreedSize=0.05M 07-31 18:37:19.081: INFO/Epic(3118): GlobalExternalAllocSize=0.00M 07-31 18:37:19.081: INFO/Epic(3118): GlobalExternalFreedSize=0.00M 07-31 18:37:19.081: INFO/Epic(3118): EpicPixels=17.9M 07-31 18:37:19.081: INFO/Epic(3118): NativeHeapSize=22.2M 07-31 18:37:19.081: INFO/Epic(3118): NativeHeapFree=0.07M 07-31 18:37:19.081: INFO/Epic(3118): NativeHeapAllocSize=22.0M 07-31 18:37:19.081: INFO/Epic(3118): ThreadAllocSize=0.12M 07-31 18:37:19.081: INFO/Epic(3118): totalMemory()=5.7M 07-31 18:37:19.081: INFO/Epic(3118): maxMemory()=24.0M 07-31 18:37:19.081: INFO/Epic(3118): freeMemory()=1.6M 07-31 18:37:19.081: INFO/Epic(3118): app.mi.availMem=126.5M 07-31 18:37:19.081: INFO/Epic(3118): app.mi.threshold=16.0M 07-31 18:37:19.081: INFO/Epic(3118): app.mi.lowMemory=false 07-31 18:37:19.081: INFO/Epic(3118): dbg.mi.dalvikPrivateDirty=0.00M 07-31 18:37:19.081: INFO/Epic(3118): dbg.mi.dalvikPss=0.00M 07-31 18:37:19.081: INFO/Epic(3118): dbg.mi.dalvikSharedDirty=0.00M 07-31 18:37:19.081: INFO/Epic(3118): dbg.mi.nativePrivateDirty=0.00M 07-31 18:37:19.081: INFO/Epic(3118): dbg.mi.nativePss=0.00M 07-31 18:37:19.081: INFO/Epic(3118): dbg.mi.nativeSharedDirty=0.00M 07-31 18:37:19.081: INFO/Epic(3118): dbg.mi.otherPrivateDirty=0.02M 07-31 18:37:19.081: INFO/Epic(3118): dbg.mi.otherPss0.02M 07-31 18:37:19.081: INFO/Epic(3118): dbg.mi.otherSharedDirty=0.00M 07-31 18:37:19.081: ERROR/dalvikvm-heap(3118): 1111200-byte external allocation too large for this process. 07-31 18:37:19.081: ERROR/dalvikvm(3118): Out of memory: Heap Size=6535KB, Allocated=4247KB, Bitmap Size=17767KB 07-31 18:37:19.081: ERROR/GraphicsJNI(3118): VM won't let us allocate 1111200 bytes
用这个小飞贼,对我有用

/**
 * 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;        
        }

我尝试了Application.onLowMemory()。不幸的是,在我的segfault崩溃的复制中,它从未被调用。这与我最终使用的代码非常相似。我使用bitmap.recycle()来释放空间,而不是返回true/false…我尝试了。。。但是如果我使用checkBitmapFitsInMemory(),它将返回false;如果我不使用checkBitmapFitsInMemory(),应用程序将加载位图而不会发生任何崩溃。我的记录值:bmpwidth=93,bmpheight=131,bmpdensity=2,reqsize=24366,heapPad=4194304,allocNativeHeap=33150304,maxMemory=33554432。你知道如何编写一个类似的方法,但效果更好吗?这段代码在Honeycom和更高版本上不起作用,因为位图不再存储在本机堆上了。@vomitcuddle发布Honeycom之后,你要做什么才能更新这段代码,使其正常工作。这只是对Debug.getNativeHeapLocatedSize()的不同调用,并用其他内容替换它吗?另外,您在上面定义的常量是什么。1024个组件表示纹理大小,4个组件表示每像素字节的配置数,0.1只是一个任意数,这样我们就不会将其加载到超过十分之一的可用内存中?@dpk-toMib是一个将大字节计数格式化为更友好的“12.3mib”的助手。。。MiB是更迂腐的MB形式,它意味着base-1024 SI单位而不是base-1000。我可能没有意识到我使用了它,也没有粘贴它。您可以删除它或使其成为您自己喜爱的格式,而不影响代码的意图。
        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;        
        }