内存溢出(OOM)
应用所需要的内存超过这个系统分配的内存限额,这就造成了内存溢出而导致应用Crash
内存泄漏
当一个对象已经不需要再使用了,本该被回收时,而另外一个正在使用的对象持有它的引用从而导致它不能被回收,这就导致本该被回收的对象不能被回收而停留在堆内存中,导致内存泄漏
关系:
内存泄漏是造成内存溢出的主要原因之一,因此在开发中应避免内存泄漏。
常见几种造成内存泄漏的案例:
一、单例造成的内存泄漏
单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。
/**
* 1、传入的是Application的Context:这将没有任何问题,因为单例的生命周期和Application的一样长 ;
* 2、传入的是Activity的Context:当这个Context所对应的Activity退出时,由于该Context和Activity
* 的生命周期一样长(Activity间接继承于Context),所以当前Activity退出时它的内存并不会被回收,
* 因为单例对象持有该Activity的引用,从而造成内存泄漏。
*/
public class AppManager {
private static AppManager instance;
private Context mContext;
private AppManager(Context context){
mContext = context;
}
public static AppManager getInstance(Context context) {
if (instance == null) {
synchronized (instance){
if(instance!=null){
instance = new AppManager(context);
}
}
}
return instance;
}
}
二、非静态内部类创建静态实例造成的内存泄漏
非静态的内部类会持有外部类的一个隐式引用
/**
* 非静态内部类持有外部类的隐式引用
* 而用该非静态内部类创建 静态的实例
* 该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用
*/
public class WorkActivity extends JYActivity{
private static PersonBean person = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(person!=null){
person = new PersonBean();
}
//...
}
class PersonBean{
//...
}
}
三、Handler造成的内存泄漏
Handler的使用编码不规范即有可能造成内存泄漏
/**
* 非静态内部类持有外部类的隐式引用
* 由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用
* 消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时,
* 消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用
* mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏
*/
public class WorkActivity extends JYActivity{
private TextView tvShow;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tvShow.setText(msg.arg1+"测试");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvShow = (TextView) this.findViewById(R.id.tv_item);
loadData();
}
private void loadData() {
Message msg = mHandler.obtainMessage();
mHandler.sendMessage(msg);
}
}
正确写法
/**
* 非静态内部类持有外部类的隐式引用
* 创建静态的内部类,弱引用Activity,从而可以持有Activity实例来做更新UI操作
* 在onDestroy方法中移除消息队列中的消息,从而更好的避免了内存泄漏
*
*/
public class WorkActivity extends JYActivity{
private TextView tvShow;
private MyHandler mHandler = new MyHandler(this);
public static class MyHandler extends Handler{
private WeakReference<Context> wrf;
public MyHandler(Context context){
wrf = new WeakReference<Context>(context);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
WorkActivity temp = (WorkActivity) wrf.get();
if(temp!=null){
temp.tvShow.setText(msg+"msg");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvShow = (TextView) this.findViewById(R.id.tv_item);
loadData();
}
private void loadData() {
Message msg = mHandler.obtainMessage();
mHandler.sendMessage(msg);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
四、线程造成的内存泄漏
/**
* 非静态内部类持有外部类的隐式引用
* 子线程内部类持有Activity的引用,当活动关闭时,子线程任务还没有执行完毕
* 导致Activity的内存资源无法回收,造成内存泄漏
*/
public class WorkActivity extends JYActivity{
private TextView tvShow;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvShow = (TextView) this.findViewById(R.id.tv_item);
new Thread(new Runnable() {
@Override
public void run() {
//...耗时操作
}
}).start();
}
}
/**
* 非静态内部类持有外部类的隐式引用
* 子线程内部类持有Activity的引用,当活动关闭时,子线程任务还没有执行完毕
* 导致Activity的内存资源无法回收,造成内存泄漏
*/
public class WorkActivity extends JYActivity{
private TextView tvShow;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvShow = (TextView) this.findViewById(R.id.tv_item);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
//...耗时操作
return null;
}
}.execute();
}
}
如何避免
/**
* 非静态内部类持有外部类的隐式引用
* 定义静态内部类,避免内存泄漏
*/
public class WorkActivity extends JYActivity{
private TextView tvShow;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvShow = (TextView) this.findViewById(R.id.tv_item);
new Thread(new MyRunnable()).start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
//...耗时操作
}
}
}
/**
* 非静态内部类持有外部类的隐式引用
* 定义静态内部类,+ 若引用 避免内存泄漏
*/
public class WorkActivity extends JYActivity{
private TextView tvShow;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvShow = (TextView) this.findViewById(R.id.tv_item);
new MyAsyncTask(this).execute();
}
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) {
//... 耗时操作
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
MainActivity activity = (MainActivity) weakReference.get();
if (activity != null) {
//...
}
}
}
}
五、资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
六、属性动画为及时停在
属性动画未在onDestory中停止,造成动画一直播放下去(即使在界面上看不到动画效果),活动的view被动画持有,view持有Activity的引用,最终导致Activity无法释放,造成内存泄漏