OutOfMemoryError虽然vm有足够的可用内存

ct_*_*rob 7 android android-memory

我得到这个奇怪的OutOfMemoryError,虽然dalvikvm报告了足够的堆空间.日志:

12-09 14:16:05.527: D/dalvikvm(10040): GC_FOR_ALLOC freed 551K, 21% free 38000K/47687K, paused 173ms, total 173ms
12-09 14:16:05.527: I/dalvikvm-heap(10040): Grow heap (frag case) to 38.369MB for 858416-byte allocation
12-09 14:16:05.699: D/dalvikvm(10040): GC_FOR_ALLOC freed 6K, 21% free 38832K/48583K, paused 169ms, total 169ms
12-09 14:16:05.894: D/dalvikvm(10040): GC_FOR_ALLOC freed 103K, 20% free 38929K/48583K, paused 169ms, total 169ms
12-09 14:16:05.894: I/dalvikvm-heap(10040): Forcing collection of SoftReferences for 858416-byte allocation
12-09 14:16:06.074: D/dalvikvm(10040): GC_BEFORE_OOM freed 6K, 20% free 38922K/48583K, paused 182ms, total 182ms
12-09 14:16:06.074: E/dalvikvm-heap(10040): Out of memory on a 858416-byte allocation.
12-09 14:16:06.074: I/dalvikvm(10040): "AsyncTask #2" prio=5 tid=17 RUNNABLE
12-09 14:16:06.074: I/dalvikvm(10040):   | group="main" sCount=0 dsCount=0 obj=0x42013580 self=0x5f2a48d8
12-09 14:16:06.074: I/dalvikvm(10040):   | sysTid=10101 nice=10 sched=0/0 cgrp=apps/bg_non_interactive handle=1591062136
12-09 14:16:06.074: I/dalvikvm(10040):   | schedstat=( 7305663992 4216491759 5326 ) utm=697 stm=32 core=1
12-09 14:16:06.074: I/dalvikvm(10040):   at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
12-09 14:16:06.074: I/dalvikvm(10040):   at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:619)
12-09 14:16:06.074: I/dalvikvm(10040):   at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:691)
Run Code Online (Sandbox Code Playgroud)

正如你在outofmemory发生之前所看到的那样,dalvikvm在gc之后报告了大约10mb的可用内存.分配用于800k位图.我怀疑gc和位图解码之间存在竞争条件,因为报告的dalvik空闲内存在崩溃前的最后20-30秒的所有日志语句中都没有降到8mb以下.

问题出现在运行Android 4.1.2的三星Galaxy Tab 2 10.1上.我正在使用Google I/O应用程序(2012)中的ImageFetcher类的修改版本,因此在加载图像以优化sampleSize选项时,我已经在使用像inJustDecodeBounds这样的东西.

根据管理位图内存中的文档,Android在dalvik堆中分配位图像素数据(自Android 3.0起),那么为什么解码位图会导致10mb可用内存的内存

有没有人见过这个或者可能知道发生了什么?

编辑:这里的每个请求是来自谷歌I/O应用程序2012的图像加载代码.在我的应用程序中,我只是打电话

mImageFetcher.loadImage(myUrl, myImageView);
Run Code Online (Sandbox Code Playgroud)

EDIT2:从上面的链接中提取的相关图像解码方法,以明确我已经在使用样本大小优化:

public static Bitmap decodeSampledBitmapFromDescriptor(
        FileDescriptor fileDescriptor, int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth,
            reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory
            .decodeFileDescriptor(fileDescriptor, null, options);
}

public static int calculateInSampleSize(BitmapFactory.Options options,
        int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        // Calculate ratios of height and width to requested height and
        // width
        final int heightRatio = Math.round((float) height
                / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will
        // guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

        // This offers some additional logic in case the image has a strange
        // aspect ratio. For example, a panorama may have a much larger
        // width than height. In these cases the total pixels might still
        // end up being too large to fit comfortably in memory, so we should
        // be more aggressive with sample down the image (=larger
        // inSampleSize).

        final float totalPixels = width * height;

        // Anything more than 2x the requested pixels we'll sample down
        // further.
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;

        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
    }
    return inSampleSize;
}
Run Code Online (Sandbox Code Playgroud)

Leo*_*Dev 0

这段代码将帮助你完成,试试这个

public Bitmap decodeSampledBitmapFromResource(String path,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(path, options);
    // BitmapFactory.decodeResource(getResources(), id)
    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth,
            reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(path, options);
}

public int calculateInSampleSize(BitmapFactory.Options options,
        int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        // Calculate ratios of height and width to requested height and
        // width
        final int heightRatio = Math.round((float) height
                / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will
        // guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }

    return inSampleSize;
}
Run Code Online (Sandbox Code Playgroud)