美文网首页Android开发收藏夹Android 轮子Android开发精选
讲讲Android开发中超常见的内存泄露以及解决

讲讲Android开发中超常见的内存泄露以及解决

作者: HuDP | 来源:发表于2015-07-13 13:27 被阅读2370次

有没有感觉自己的Android手机开机之后,可用内存不断减少,很大一部分原因就是因为内存泄露了,这部分泄露的内存无法被回收,所以可用内存越来越少;

什么是内存泄露?GC无法回收原本应该被回收的对象,这个对象就引发了内存泄露

1.Bitmap使用完忘记回收,因为bitmap实现部分是通过JNI调用了Native方法,GC机制无法正常回收 Bitmap申请的这部分内存空间(API10之前是这样的,之后分配在Heap中,不过为了兼容老版本...显示的调用一下recycled,让对象变为虚引用,也能让GC到来的几率更高);

那Bitmap应该怎样回收呢?

// bitmap!=null要放在前面,如果bitmap为空再去调用isRecycled方法便会报错

if(bitmap!=null&&!bitmap.isRecycled){

      bitmap.recycled(); //回收bitmap

      bitmap = null;      //使bitmap对象变为虚引用的状态,让GC更快的回收

}

2.万恶的static;此话怎讲,被static修饰的变量,生命周期与整个App的生命周期相同,需要谨慎使用它来修饰一些耗费资源过多的实例

举个栗子:

private static Drawable sBackground;

@Override

protected void onCreate(Bundle state) {

    super.onCreate(state);

    TextView label =new TextView(this);

    label.setText("Leaks are bad");

    if(sBackground ==null) {

        sBackground = getDrawable(R.drawable.large_bitmap);

    }

    label.setBackgroundDrawable(sBackground);

    setContentView(label);

}

这里的sBackground是个静态变量,也就是说就算Activity销毁掉了,他也无法被释放,就导致了内存浪费,当然这还没完... 当Drawable与View绑定之后,Drawable就将View设置为一个回调,由于View中是包含了Context引用的...最终就是 Context发生内存泄露;

3.Context的引用问题;当然2中也提到了一部分,咱们再看看这里的

btn_hint.setOnClickListener(new View.OnClickListener() {

@Override

    public void onClick(View v) {

        Toast.makeText(MainActivity.this, "Hello", Toast.LENGTH_SHORT).show();

   }

});

是不是很常见,平时可能也是这样写的(将其封装意义一样,只要引用了当前activity),你可能想问,这里有什么问题吗?

问题在于如果用户在Toast消失之前,用户按了返回键,这个Activity就引起了内存泄露,

原因? Toast持有了当前Activity,导致Activity无法被GC销毁

解决方法:让Toast持有ApplicationContext;其实只要不是Layout,Context都可以使用ApplicationContext;

顺便讲个小技巧:在非Activity中,正常是不能直接getContext来拿到Context的,获取资源有需要靠Context,这时可以考虑在自己的Application中维护一个全局的Context,供无法直接拿到Context的类使用,省的参数传来传去(视图相关的不建议使用ApplicationContext)

private static Context mContext;

public static MyApplication getInstance() { //供外界调用...

     return mApplication;

}

@Override

public void onCreate() {

    super.onCreate(); 

    mContext = getApplicationContext();   

}


4.线程引发的内存泄露


new Thread() {

    public void run() {

    //网络请求

    };

}.start();

这里也有问题?你tm逗我?

在Activity中 新建一个线程,进行网络请求,如果线程未结束,用户按了返回键,同样内存泄露

原因:该Thread是匿名内部类,所以会隐式的持有外部类(这里也就是Activity)

解决方式:多种多样; 不使用匿名内部类,或者整个应用维护一个线程池,或者维护一个线程+消息队列,后两种都是让线程不依赖于Activity从而达到避免内存泄露的目的;

总结:

资源获取等等很多地方都需要用到Context,很多地方都会用到匿名内部类,这也就导致了这里存在很大的内存泄露隐患,但平时很多小伙伴可能还没有注意到,希望这篇文章能给你一些启发。

如果对内容有任何疑问或者建议,欢迎留言或者联系作者。Email:mox113@foxmail.com

相关文章

网友评论

  • 恨自己不能小清新:博主能重点讲下4.线程引发的内存泄露么?这个不太懂其他可以看懂 :blush:
    HuDP:@恨自己不能小清新 java中 内部类会隐式持有外部类 在这个例子中 外部类就是activity ,如果thread还在运行的时候 activity关闭了 因为thread持有了activity的强引用,所以activity无法回收,且也没有正常方式可以回收了
  • 追风骚年:早就在思考那个Context的问题 我也觉得全局都用Application的context就行 每次传参数都传一个context就是愚蠢
    HuDP:@追风骚年 看情况 构建ui相关的 除了系统的Window (toast) 其他都是需要activity的context
  • 小鄧子:@HuDP 我明天,在看一下,已经躺下了,早点休息。明天一起探讨。😮
  • HuDP:@小鄧子 恩恩... 变成了虚引用 挂的几率高些 :joy:
  • 小鄧子:@HuDP 解除了二进制的强引用,提高gc的回收几率。
  • HuDP:@小鄧子
    if (nativeRecycle(mNativeBitmap)) {
    mBuffer = null;
    mNinePatchChunk = null;
    }
    把两个byte[] 赋为null 能让自己被GC回收的概率加大
  • 小鄧子:@HuDP 这个方法跟system.gc 类似了,只能显式的提示GC回收,但频繁的提示,也会对GC造成压力。
  • HuDP:@小鄧子 的确... 为了兼容2.x版本 大家依然都手写这个了...
  • 小鄧子:@HuDP 嗯哼,就是这个意思。
  • HuDP: @小鄧子 是说3.0之后bitmap部分的内存分配到了 堆中吗?
  • HuDP:@小鄧子 可否详细些?
  • 小鄧子:然而bitmap.recycled();并没有太大用处
    imesong:@HuDP 3.0 之后 bitmap 虽然存储在java heap 中,系统只会在内存紧张时自动回收,调用bitmap.recycle() 可以主动释放 java heap 中的内存,提高回收效率
    HuDP:@imesong 看评论
    imesong:@小鄧子 为什么说没用呢?
  • HuDP:@Evasi0n 不涉及Layout视图方面的 都可以使用全局的Context 就是getApplicationContext()
    不用每次都去获取
    private Context mContext;
    然后初始化的时候
    mContext = getApplicationContext();

    也可以考虑把这个写在Application 然后公开一个返回值为Context的方法让外部调用,这样就可以让非Activity也可以直接拿到Context
  • 这条鱼有点甜:你说是用getApplicationContext()代替吗?
  • HuDP:@你是我de符合 好吧好吧... 那谢谢观看啦...
  • 你是我de符合:抱歉,忘了说明我是安卓系统开发什么的门外汉,我应该是全不懂
  • HuDP:@你是我de符合 能否具体些呢?

本文标题:讲讲Android开发中超常见的内存泄露以及解决

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