Android开发简记:探索App性能优化之Android内存泄漏,透彻分析源码

2、本质原因

本该被回收的对象因为某些原因而不能被回收,从而继续停留在堆内存中。当一个对象已不需再本该被GC回收时,但有另一个正在使用的对象持有它的引用,从而导致它不能被GC回收而停留在堆内存中。即长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏.

四、Android内存泄露主要原因

(一)static关键字修饰的成员变量

1、static修饰context造成的内存泄漏

问题描述:static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。用static这个关键字修饰变量,使得变量的生命周期大大延长,并且访问的时候,也极其的方便,用类名就能直接访问,各个资源间传值也极其的方便,所以它经常被使用。但如果用它来引用一些资源耗费过的实例(Context的情况最多),这时就要谨慎对待了。

public class ClassName {

private static Context mContext;

//省略

}

以上的代码是很危险的,如果将Activity赋值到mContext的话。那么即使该Activity已经onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放。如果该Activity里面再持有一些资源,那就更糟糕了。

解决办法:

1.尽量避免 Static 成员变量引用资源耗费过多的实例(如Context)。

2.使用弱引用WeakReference代替强引用持有实例。比如可以使用WeakReference mContextRef。

2、单例造成的内存泄漏

问题描述:Android的单例模式使用的不恰当会造成内存泄漏。因为单例的静态特性使得单例的生命周期和应用的生命周期一样长,如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。

public class BankManager {

private static BankManager instance;

private Context context;

private BankManager(Context context) {

this.context = context;

}

public static BankManager getInstance(Context context) {

if (instance == null) {

instance = new BankManager(context);

}

return instance;

}

}

以上是一个普通的单例模式,当创建这个单例的时候,需要传入一个Context,所以这个Context的生命周期的长短至关重要。

  • 传入的是Application的Context:单例的生命周期和Application的一样长,这将没有任何问题;

  • 传入的是Activity的Context:由于该Context和Activity的生命周期一样长(Activity间接继承于Context),单例的生命周期可能大于Activity的生命周期。当这个Context所对应的Activity退出时它的内存并不会被回收,因为单例对象持有该Activity的引用。

解决办法:

Context 尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。

正确的单例应该为下面的方式:

public class BankManager {

private static BankManager instance;

private Context context;

private BankManager(Context context) {

this.context = context.getApplicationContext();

}

public static BankManager getInstance(Context context) {

if (instance != null) {

instance = new BankManager(context);

}

return instance;

}

}

(二)非静态内部类/匿名类

1、非静态内部类创建静态实例造成的内存泄漏

问题描述:在启动频繁的Activity中,为了避免重复创建相同的数据资源,会在Activity内部创建一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据。若非静态内部类所创建的实例的生命周期等于应用的生命周期,会因非静态内部类持有外部类的引用,而导致外部类无法释放,最终造成内存泄露。

public class MainActivity extends AppCompatActivity {

private static TestResource mResource = null;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

if(mManager == null){

mManager = new TestResource();

}

//…

}

private class TestResource {

//…

}

}

以上代码就在Activity内部创建了一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,却会造成内存泄漏。非静态内部类会持有外部类的引用,而又使用非静态内部类创建静态实例,该实例和应用的生命周期一样长,这就导致该静态实例一直会持有Activity的引用,导致Activity的内存资源不能回收。

解决办法:

1.将非静态内部类改为静态内部类(静态内部类默认不持有外部类的引用)

2.该内部类抽取出来封装成一个单例。若需使用Context,建议使用 Application 的 Context

2、Handler造成的内存泄漏

问题描述:Handler的使用造成的内存泄漏问题应该最为常见,在处理网络任务或者封装一些请求回调等api都应该会借助Handler来处理,对于Handler的使用代码编写一不规范即有可能造成内存泄漏,如下示例:

public class MainActivity extends AppCompatActivity {

private Handler mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

//…doSomething

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

loadData();

}

private void loadData(){

//…request http

Message message = Message.obtain();

mHandler.sendMessage(message);

}

}

这种创建Handler的方式会造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,消息队列MessageQueue在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时,消息队列中还有未处理的消息Message或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。

解决办法:

1.创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了Activity泄漏。

2.Looper线程的消息队列中还是可能会有待处理的消息,所以在Activity的Destroy时或者Stop时应该移除消息队列中的消息。

public class MainActivity extends AppCompatActivity {

private MyHandler mHandler;

private TextView mTextView

如果你进阶的路上缺乏方向,可以点击我的【Github】加入我们的圈子和安卓开发者们一起学习交流!
以下全部内容都可以在GitHub中获取!

  • Android进阶学习全套手册

    img

  • Android对标阿里P7学习视频

    img

  • BATJ大厂Android高频面试题

    img

最后,借用我最喜欢的乔布斯语录,作为本文的结尾:

人这一辈子没法做太多的事情,所以每一件都要做得精彩绝伦。
你的时间有限,所以不要为别人而活。不要被教条所限,不要活在别人的观念里。不要让别人的意见左右自己内心的声音。
最重要的是,勇敢的去追随自己的心灵和直觉,只有自己的心灵和直觉才知道你自己的真实想法,其他一切都是次要。

droid对标阿里P7学习视频

[外链图片转存中…(img-M3weIKnP-1645099060747)]

  • BATJ大厂Android高频面试题

    [外链图片转存中…(img-FWtw4Wiq-1645099060747)]

最后,借用我最喜欢的乔布斯语录,作为本文的结尾:

人这一辈子没法做太多的事情,所以每一件都要做得精彩绝伦。
你的时间有限,所以不要为别人而活。不要被教条所限,不要活在别人的观念里。不要让别人的意见左右自己内心的声音。
最重要的是,勇敢的去追随自己的心灵和直觉,只有自己的心灵和直觉才知道你自己的真实想法,其他一切都是次要。

猜你喜欢

转载自blog.csdn.net/m0_66264630/article/details/122990786
今日推荐