android面试-内存泄漏(美图、久邦面涉及到)

一、Android中会造成内存泄露的情景无外乎两种:

    • 全局进程(process-global)的static变量。这个无视应用的状态,持有Activity的强引用的怪物。
    • 活在Activity生命周期之外的线程。没有清空对Activity的强引用。
  • 参考文章:

  • Android内存泄露——全解析和处理办法http://www.jianshu.com/p/bf159a9c391a
  • 5大内存泄漏问题及解决办法http://www.maiziedu.com/article/9126/
  • 最最最重要的知识点关联,面试官可能会问你那你怎么去处理内存泄漏或者使用什么工具去发现内存泄漏,处理内存泄漏上述链接以及面的内容讲了如何的处理,而使用什么工具?LeakCanary,具体的操作使用详见 http://blog.csdn.net/qq_27258799/article/details/51012305
  • 二、注意可能造成的主要五种对应解决措施

  • 1、单例造成的内存泄漏
  • 单例静态特性使得单例的生命周期与应用的生命周期一样长,如果引用了某个活动的context,当活动结束了却没结束引用,会造成泄漏,
  • 办法是传入ApplicationContext,

2、 频繁启动的Activity中非静态内部类创建静态实例
  • 为了避免重复创建资源常会使用单例进行创建, 因为非静态内部类默认会持有外部类的引用,而又使用了该非静态内部类创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。
  • 方法:将该内部类封装成一个实例
    • ps:静态内部类和非静态内部类的区别http://blog.csdn.net/fgakjfd/article/details/5282646 
    • 如果你不需要内部类对象与其外围类对象之间有联系,那你可以将内部类声明为static。
    • 一 . 静态内部类可以有静态成员,而非静态内部类则不能有静态成员。 
      二 . 静态内部类的非静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量;

      三 . 非静态内部类的非静态成员可以访问外部类的非静态变量。

          生成一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。

3、Handler造成的内部泄漏
  • mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,(消息有延滞性)而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏,
  • public class MainActivity extends AppCompatActivity {

        private MyHandler mHandler = new MyHandler(this);

        private TextView mTextView ;

        private static class MyHandler extends Handler {

            private WeakReference<Context> reference;

            public MyHandler(Context context) {

                reference = new WeakReference<>(context);

            }

            @Override

            public void handleMessage(Message msg) {

                MainActivity activity = (MainActivity) reference.get();

                if(activity != null){

                    activity.mTextView.setText("");

                }

            }

        }

     

        @Override

        protected void onCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);

            setContentView(R.layout.activity_main);

            mTextView = (TextView)findViewById(R.id.textview);

            loadData();

        }

     

        private void loadData() {

            //...request

            Message message = Message.obtain();

            mHandler.sendMessage(message);

        }

    }
  • 创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该移除消息队列中的消息
  •  @Override

        protected void onDestroy() {

            super.onDestroy();

            mHandler.removeCallbacksAndMessages(null);

        }

 4、 AsyncTask
  • //——————test1
            new AsyncTask<Void, Void, Void>() {

                @Override

                protected Void doInBackground(Void... params) {

                    SystemClock.sleep(10000);

                    return null;

                }

            }.execute();

    //——————test2

            new Thread(new Runnable() {

                @Override

                public void run() {

                    SystemClock.sleep(10000);

                }

            }).start();

     
  • 上面的异步任务和Runnable都是一个匿名内部类,因此它们对当前Activity都有一个隐式引用。如果Activity在销毁之前,任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏。
  • 办法:正确的做法还是使用静态内部类的方式,如下:

     

        static class MyAsyncTask extends AsyncTask<Void, Void, Void> {

            private WeakReference<Context> weakReference;

     

            public MyAsyncTask(Context context) {

                weakReference = new WeakReference<>(context);

            }

     

            @Override

            protected Void doInBackground(Void... params) {

                SystemClock.sleep(10000);

                return null;

            }

     

            @Override

            protected void onPostExecute(Void aVoid) {

                super.onPostExecute(aVoid);

                MainActivity activity = (MainActivity) weakReference.get();

                if (activity != null) {

                    //...

                }

            }

        }

        static class MyRunnable implements Runnable{

            @Override

            public void run() {

                SystemClock.sleep(10000);

            }

        }

    //——————

        new Thread(new MyRunnable()).start();   
        new MyAsyncTask(this).execute();

5、资源未关闭造成的内存泄漏
  • 对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。

猜你喜欢

转载自blog.csdn.net/wzhworld/article/details/78318433