【Android】安卓常见的内存泄漏:OOM,bitmap

一、关于OOM

1. android图片加载导致的OOM分析及有效解决办法(BitmapUtils)

[摘] OOM原因分析

android每一个应用都有一个独立的进程,每个进程都是实例化了dalvik虚拟机实例的linux进程。Dalvik 主要管理的内存有 Java heap 和 native heap 两大块。Android系统对dalvik的vm heapsize作了硬性限制,当java进程申请的java空间超过阈值时,就会抛出OOM异常(这个阈值可以是48M、24M、16M等,视机型而定),而native heap大小则是不受次限制的。

当我们需要显示大的bitmap对象或者较多的bitmap的时候,如果使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置图片,则容易出现OOM,这个是因为bitpmap分配内存受限于java heap大小(一张在1024*1024图片,假设照片是用ARGB_8888格式,那么需要占用4M的内存存储像素点信息, bitmap分辨率越高,所占用的内存就越大,这个是以2为指数级增长的。)。此处引出一个问题:bitmap对象是分配在native heap还是java heap上的呢?后续会在单独一篇文章中讲解(Bitmap分配在java heap还是native heap)

2. android Bitmap分配在java heap还是native heap

在android 2.3和以前的版本,bitmap对象的像素数据都是分配在native heap中的,所以我们在调试过程中这部分内存是在java heap中看不到的,不过在android 3.0之后,bitmap对象就直接分配在java heap上了,这样便于调试和管理。因此在3.0之后我们可以复用bitmap的内存,而不必回收它,不过新的bitmap对象要大小和原来的一样,到了android 4.4之后,就只要高宽不超过原来的就行了。Bitmap是分配在dalvik heap上的,只有这样才能解释bitmap容易导致OOM。

3. Bitmap的有关讲解与优化

二、常见的问题

1. Attempt to invoke virtual method ‘int android.graphics.Bitmap.getWidth()’ on a null object reference

refer to 参考链接

问题描述
该问题通常是在调用getWidth() 方法时,对应的Bitmap有时候出现为null,导致getWidth()方法出现空指针。

可能的原因

  1. No read permission
  2. The image file is corrupt
  3. There is not enough memory to decode the file
  4. The resource does not exist
  5. Invalid options specified in the options variable.

建议的调试方法:
通过引入if(unscaledBitmap == null)的判断,进行断点调试。

public class BitmapScalingHelper
{
    public static Bitmap decodeResource(Resources res, int resId, int dstWidth, int dstHeight)
    {
        Options options = new Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
        options.inJustDecodeBounds = false;

        options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth,
                dstHeight);

        options = new Options(); 
        //May use null here as well. The funciton may interpret the pre-used options variable in ways hard to tell.
        Bitmap unscaledBitmap = BitmapFactory.decodeResource(res, resId, options);

        if(unscaledBitmap == null)
        {
            Log.e("ERR","Failed to decode resource - " + resId + " " + res.toString());
            return null;
        }

        return unscaledBitmap;
    }
}

可以尝试的解决办法:

  • 通过BitmapFactory.decodeByteArray把byte[]转成Bitmap出现的OOM的解决方法
    • 替换成SoftReference softRef = new SoftReference(BitmapFactory.decodeStream( input, null, options));
  • Android - BitmapFactory.decodeByteArray - OutOfMemoryError (OOM):
    • 引入System.gc();

2. Free memory of a byte array in Java

byte[] myArray = new byte[54];
myArray = null;
yourArray = myArray;
yourArray = null;

三、测试相关

3.1 测试命令

while true; do dumpsys meminfo 23637 | grep "TOTAL SWAP PSS:"; sleep 2; done,
while true; do dumpsys -t 600 meminfo --unreachable 7898 | grep "unreachable allocations" ; sleep 1; done

3.2 测试命令的差异

前者监控的是实际占用的内存,后者unreachable allocations监控的是(需要java的垃圾管理器要去释放的内存)

3.3 测试相关资料

Android系统查看某个进程的线程
linux下top命令参数解释
垃圾回收机制与调用System.gc()区别

四、测试结论

JNIEXPORT jint JNICALL Java_org_apache_tvm_LibInfo_tvmArrayCopyFromJArray(
  JNIEnv *env, jobject obj, jbyteArray jarr, jlong jfrom, jlong jto) {
  jbyte *data = env->GetByteArrayElements(jarr, NULL);
  DLTensor *from = reinterpret_cast<DLTensor*>(jfrom);
  //from->data = static_cast(data);
  memcpy(from->data, static_cast<void *>(data), 150528);
  int ret = TVMArrayCopyFromTo(static_cast<TVMArrayHandle>(from),
                               reinterpret_cast<TVMArrayHandle>(jto), NULL);
  free(from->data);
  from->data = NULL;
  env->ReleaseByteArrayElements(jarr, data, 0);
  ret

五、其他资料

pthread_create failed: couldn’t allocate 1069056-bytes mapped space: Out of memory
Android 启动线程OOM
Android Framework中的线程Thread及它的threadLoop方法
JNI的原理及内存详解

你可能感兴趣的:(Android小白教程)