Android内存管理

2016-04-01  本文已影响958人  14cat

1. Android中的内存

1.1 Android中的垃圾回收机制

Android 平台最吸引开发者的一个特性:有垃圾回收机制,无需手动管理内存,Android 系统会自动跟踪所有的对象,并释放那些不再被使用的对象

1.2 垃圾回收

1.3 垃圾回收机制&FPS

1.4 内存泄漏

在整个Android开发过程中,内存泄漏是导致OOM(Out Of Memory内存溢出)的一个重要因素

  1. 应用程序分配了大量不能回收的对象
  2. 这导致可分配的内存越来越少
  3. 当新对象的创建需要的内存不够
  4. 当发现内存不够就会调用一次GC进行垃圾回收
  5. 结果:就会发生卡顿

1.5 内存抖动

原因:内存抖动是因为应用程序在短时间内创建大量的对象,又被马上释放。

  1. 瞬间产生大量的对象会严重占用Young Generation的内存区域

2. 内存检测工具

2.1 Memory Monitor 内存监视器

2.2 Allocation Tracker 分配跟踪器

2.3 Heap Viewer 堆视图

2.4 Leak Canary

https://github.com/square/leakcanary


3. 常见的内存泄漏问题

3.1 单例造成的泄漏

将Context对象保存在单例模式中,instance对象本身持有一个Context对象的引用,活动即时被销毁也不能被回收,因为静态变量一直持有它的引用

public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}

可以改为

public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
            
        //    使用Application的Context(也可以用自定义的Application)
        this.context = context.getApplicationContext();        
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}

3.2 非静态内部类的静态实例造成的泄漏

静态的sResource在创建时会间接持有一个MainActivity实例的引用,导致MainActivity无法被回收

public class MainActivity extends Activity {
    private static TestResource sResource = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (sResource == null) {
            sResource = new TestResource();
        }
        // ...
    }

    // 非静态内部类
    class TestResource {
        // ...
    }
}

将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例

如果用到Context就使用Application的Context

但是Dialog不能使用Application和Service的Context

3.3 Handler 造成的内存泄漏问题

当创建匿名对象时,该对象会间接持有外部类实例的一个引用,mHandler对象本身会持有MainActivity的引用,导致MainActivity销毁后无法即时被回收

public class MainActivity extends Activity {
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
    }

    private void loadData() {
        // ...request
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }
}

在Activity中避免使用非静态内部类,比如将Handler声明为静态的,这样Handler的存活时间就与Activity无关了

同时引入弱引用的方式引入Activity,避免将Activity作为Context传入

使用前判空

public class MainActivity extends Activity {
    private static class MyHandler extends Handler {
        private final WeakReference<MainActivity> mActivity;

        private MyHandler(MainActivity activity){
            mActivity = new WeakReference<MainActivity>(activity);
        }
        
        @Override
        public void handleMessage(Message msg) {
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
    }

    private void loadData() {
        // ...request
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }
}

3.4 集合类泄漏


4. 避免内存泄漏的方法

  1. 尽量不要让静态变量引用Activity
  2. 使用WeakReference弱引用,会保证GC时会被回收
  3. 使用静态内部类来代替内部类,静态内部类不持有外部类的引用
  4. 静态内部类使用弱引用来引用外部类
  5. 在声明周期结束的时候释放资源

5. 减少内存使用

  1. 使用更轻量的数据结构(SpareArray代替HashMap)

    • Google自己定义的类占用内存更小
  2. 避免在onDraw方法中创建对象

    • onDraw()方法被频繁调用,在其中创建对象会导致临时对象过多,发生内存抖动
  3. 对象池(Message.obtain())

    • 当一定要在onDraw中创建对象,推荐使用对象池
    • 相当于对象缓冲,在创建时查找是否已经存在对象,没有在创建
  4. LRUCache

    • 大大减少内存使用
  5. Bitmap内存复用,压缩(inSampleSize,inBitmap)

  6. StringBuilder

    • 代替String,尤其是进行拼接操作时
上一篇下一篇

猜你喜欢

热点阅读