zoukankan      html  css  js  c++  java
  • 内存优化

    垃圾回收机制自动内存管理和回收机制):回收程序中已经不使用,但是任然被各种对象占用的内存。

    内存检测工具:
    常见的内存泄漏
    一、单例造成的内存泄漏
    public class AppManager {
        private static AppManager instance;
        private Context context;
        private AppManager(Context context) {
           this.context = context;//优化后的代码 this.context = context.getApplicationContext();
        }
        public static AppManager getInstance(Context context) {
            if (instance != null) {
                instance = new AppManager(context);
            }
            return instance;
        }
    }
    这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:
          1、传入的是Application的Context:这将没有任何问题,因为单例的生命周期和Application的一样长 ;
          2、传入的是Activity的Context:当这个Context所对应的Activity退出时,由于该Context和Activity的生命周期一样长(Activity间接继承于Context),所以当前Activity退出时它的内存并不会被回收,因为单例对象持有该Activity的引用。正确的做法为:this.context = context.getApplicationContext();
    二、非静态内部类创建静态实例造成的内存泄漏
    public class MainActivity extends AppCompatActivity {private static TestResource mResource = null;
        @Overrideprotected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if(mManager == null){
                mManager = new TestResource();
            }
            //...
        }
        class TestResource {//...
        }
    }
    这样就在Activity内部创建了一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而又使用了该非静态内部类创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。正确的做法为:
    将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,请使用ApplicationContext 。
     三、Handler造成的内存泄漏
    public class MainActivity extends AppCompatActivity {
        private Handler mHandler = new Handler() {
            @Overridepublic void handleMessage(Message msg) {
                //...
            }
        };
        @Overrideprotected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            loadData();
        }
        private void loadData(){
            //...request
            Message message = Message.obtain();
            mHandler.sendMessage(message);
        }
    }

     这种创建Handler的方式会造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏,正确的做法为:创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,并在Activity的Destroy时或者Stop时移除消息队列中的消息

    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);
            }
            @Overridepublic void handleMessage(Message msg) {
                MainActivity activity = (MainActivity) reference.get();
                if(activity != null){
                    activity.mTextView.setText("");
                }
            }
        }
        @Overrideprotected 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);
        }
        @Overrideprotected void onDestroy() {
            super.onDestroy();
            mHandler.removeCallbacksAndMessages(null);
        }
    }
    四、线程造成的内存泄漏
    //——————test1new AsyncTask<Void, Void, Void>() {
                @Overrideprotected Void doInBackground(Void... params) {
                    SystemClock.sleep(10000);
                    return null;
                }
            }.execute();
    //——————test2new Thread(new Runnable() {
                @Overridepublic 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);
            }
            @Overrideprotected Void doInBackground(Void... params) {
                SystemClock.sleep(10000);
                return null;
            }
            @Overrideprotected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
                    MainActivity activity = (MainActivity) weakReference.get();
                if (activity != null) {
                    //...
                }
            }
        }
        static class MyRunnable implements Runnable{
            @Overridepublic void run() {
                SystemClock.sleep(10000);
            }
        }
    //——————new Thread(new MyRunnable()).start();
        new MyAsyncTask(this).execute();
    这样就避免了Activity的内存资源泄漏,当然在Activity销毁时候也应该取消相应的任务AsyncTask::cancel(),避免任务在后台执行浪费资源。
    五、资源未关闭造成的内存泄漏
     对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
    避免内存泄漏的方法:
       1.尽量不要让静态变量引用Activity
       2.使用 WeakReference
    3.使用静态内部类代替内部类
    4.静态内部类使用弱引用来引用外部类
    5.在声明周期结束的时候释放资源
    减少内存使用方法
    1.使用更轻量级的数据结构(如 使用SpareArray代替HashMap)
    2.避免在onDraw中创建对象
    3.对象池(Message.obtain())
    4.LRUCache
    5.Bitmap内存复用,压缩((inSampleSize,inBitmap)
    6.用StringBuilder代替String





  • 相关阅读:
    C# 实现 Snowflake算法生成唯一性Id
    kafka可视化客户端工具(Kafka Tool)的基本使用(转)
    docker 安装kafka
    Model类代码生成器
    使用docker 部署rabbitmq 镜像
    Vue 增删改查 demo
    git 提交代码到库
    Android ble蓝牙问题
    mac 配置 ssh 到git (Could not resolve hostname github.com, Failed to connect to github.com port 443 Operation timed out)
    okhttp
  • 原文地址:https://www.cnblogs.com/wisemen/p/5820164.html
Copyright © 2011-2022 走看看