Android 内存泄露

作者: xsp单细胞 | 来源:发表于2017-02-27 17:15 被阅读9次

    前言

    对于 c++ 来说,内存泄漏就是new出来的对象没有 delete,俗称野指针;而对于 java 来说,就是 new 出来的 object 放在 Heap 上无法被GC回收

    对象的引用方式

    • 强引用(StrongReference):new 的对象没释放,JVM 哪怕发生 OOM 错误也不会回收该对象
    • 软引用(SoftReference):GC时,当内存不够使用时才回收
    • 弱引用(WeakReference):GC时,不管内存是否够用都会回收
    • 虚引用(PhantomReference):和没有任何引用一样,随时都可能被回收

    Java 内存

    Java是在JVM所虚拟出的内存环境中运行的,JVM的内存可分为三个区:堆(heap)、栈(stack)和方法区(method)

    • 栈:栈中只存放基本类型和对象的引用(不是对象),LIFO
    • 堆:堆内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。JVM只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
    • 方法区:又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量

    垃圾回收机制

    垃圾回收(garbage collection,简称GC)可以自动清空堆中不再使用的对象。在JAVA中对象是通过引用使用的。如果再没有引用指向该对象,那么该对象就无从处理或调用该对象,这样的对象称为不可到达。垃圾回收用于释放不可到达的对象所占据的内存。
    实现思想:我们将栈定义为root,遍历栈中所有的对象的引用,再遍历一遍堆中的对象。因为栈中的对象的引用执行完毕就删除,所以我们就可以通过栈中的对象的引用,查找到堆中没有被指向的对象,这些对象即为不可到达对象,对其进行垃圾回收。

    垃圾回收机制

    内存泄露 VS 内存溢出

    内存溢出(out of memory):是指程序在申请内存时,没有足够的内存空间供其使用
    内存泄露 (memory leak):是指程序在申请内存后,无法释放已申请的内存空间

    内存泄露原因

    在JAVA中JVM的栈记录了方法的调用,每个线程拥有一个栈。在线程的运行过程当中,执行到一个新的方法调用,就在栈中增加一个内存单元,即帧(frame)。在frame中,保存有该方法调用的参数、局部变量和返回地址。然而JAVA中的局部变量只能是基本类型变量(int),或者对象的引用。所以在栈中只存放基本类型变量和对象的引用。引用的对象保存在堆中
    当某方法运行结束时,该方法对应的frame将会从栈中删除,frame中所有局部变量和参数所占有的空间也随之释放。线程回到原方法继续执行,当所有的栈都清空的时候,程序也就随之运行结束
    而对于堆内存,堆存放着普通变量。在JAVA中堆内存不会随着方法的结束而清空,所以在方法中定义了局部变量,在方法结束后变量依然存活在堆中

    常见的内存泄露

    • Bitmap使用
      图片Size。美工给的切图直接使用,很容易造成内存泄漏。所以我们就必须从源头上减少内存的开销。特别是要展示一些缩略图或者对图片质量要求不怎么高的时候,就更应该进行设置。我们可以使用BItmapFactory.Options设置inSampleSize。该参数可以设置显示图的宽高分别为原始图片大小的几分之一

        BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();
        bitmapFactoryOptions.inJustDecodeBounds = true;
        bitmapFactoryOptions.inSampleSize = 3;
        options.inJustDecodeBounds = false;
        Bitmap bmp = BitmapFactory.decodeFile(sourceBitmap,bitmapFactoryOptions); 
      

      图片像素。除了图片的大小外,图片的像素也对内存的消耗有影响,所以如果需求对图片的质量要求不是特别高就可以改下默认值,从而减少开销

      • ALPHA_8:每个像素占用1byte内存
      • ARGB_4444:每个像素占用2byte内存
      • ARGB_8888:每个像素占用4byte内存(默认)
      • RGB_565:每个像素占用2byte内存

      图片回收。在图片使用完之后,我们需要对图片进行回收,而不是等系统进行回收

        if (null != bitmap && !bitmap.isRecycled()) {
            bitmap.recyle();
            bitmap = null;
            System.gc(); // 只是建议进行垃圾回收,实际会不会回收无法预知
        }
      
    • 关闭对象

      • Cursor回收:Activity的onDestory()中对cursor进行关闭
      • 注销BroadcaseReceiver:广播一定要unregisterReceiver
      • 关闭I / O:用完close()
    • Activity

      • Context:尽量使用ApplicationContext而不是Activity,因为引用的实例的生命周期很有可能超出Activity的生命周期,从而造成Activity无法回收,导致内存泄漏
      • Listener:注册Listener时若传入Activity的引用,onDestroy时记得removeListener
      • Handler:Handler和Activity的生命周期是不一样的,所以在Activity销毁时mHandler.removeCallbacks(mRunnable);
      • 总结:凡是使用Activity对象(包括this指针)的,当Activity 销毁时记得解绑、销毁。
    • 其它

    • Activity中尽量不要定义静态常量

    • 切记不要在循环中频繁 new 对象

    工具

    • eclipse 中引入插件:MAT
    • Android Studio:Android Monitor
    • 项目集成 : LeakCanary

    相关文章

      网友评论

        本文标题:Android 内存泄露

        本文链接:https://www.haomeiwen.com/subject/npozwttx.html