简介
android 的内存泄漏,又称之为oom。即 android 系统会为每一个应用分配一定的内存空间。而 jvm 也会在不定时间间隔进行内存回收,但是当我们的代码书写的不够规范时候。导致一些内存不能够回收,累积内存超过应用所分配的最大内存即会造成内存泄漏。
了解 java 的内存
在 java 中内存环境主要分为三种:
静态存储区:存储静态变量以及全局变量数据。所有线程共享。
栈:在函数中的基本变量类型或者对象类型的引用,java在栈中为其分配空间。当函数结束时候,分配的空间进行释放。
堆:堆内存用来存放由new创建的对象和数组。 由 java gc 进行回收。所有线程进行共享。
为什么会出现内存泄漏
1 当一个对象已经不需要再使用了,需要被回收时。另外一个正在使用的对象持有了该对象导致该对象不能被回收,停留在堆内存中。
2 有些对象只有有限的生命周期,需要被回收。同时也被一系列的引用。导致不能被回收。
android 中的内存泄漏案例:
1 单例造成的内存泄漏:
public class SingleDemoInstance {
private static SingleDemoInstance instance;
private Context context;
private SingleDemoInstance(Context context) {
this.context = context;
}
public static SingleDemoInstance getInstance(Context context) {
if (instance == null) {
instance = new SingleDemoInstance(context);
}
return instance;
}
}
上面的单例模式可能造成内存泄漏,SingleDemoInstance 的静态变量生命周期为应用生命周期,假如构造单例传入的 Context 为 Activity 的 Context ,某一时刻我们想要把 Activity 回收 finish 掉。此时单例存在,并且持有了 Activity ,那么此时的 Activity 就不能被回收。可能会造成内存泄漏。
解决办法:
这里可以用 this.context = context.getApplciationContext() 代替上面 context 的赋值 ; 此时 context 的生命周期为整个程序运行期间,则不会影响到 Activity 的内存释放。
2 非静态内部类创建静态实例造成的内存泄漏:
class DemoActivity extends Activity {
public static DemoClass demo = null;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (demo == null) {
demo = new DemoClass();
}
}
private class DemoClass {
}
}
分析上述代码会出现内存泄漏的情况:
静态变量 demo 的生命周期为应用周期。
内部类 DemoClass 对外部类 DemoActivity 会持有引用。
当 DemoActivity 需要被回收释放的时候,发现 DemoClass 的一个实体变量对它进行了引用,并且生命周期为应用生命周期。故不能成功释放。可能导致内存泄漏。
解决办法:
private static class DemoClass {
}
内部类改成静态的,就不会持有对外部类的引用。不会影响到 DemoActivity 资源的释放。
3 handler 造成的内存泄漏
public class HandlerActivity extends Activity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 1000 * 60 * 10);
}
}
在开发中, handler 经常按照上述方法使用,handler 内部处理消息可能会有延迟,上述的方法是为了创造延迟环境。为什么会出现内存泄漏呢?
handler 的运行机制对 Activity 的持有,假如现在的处理消息有延迟,那么当 Activity finish 之后想要释放的时候,因为 handler 持有了 Activity ,而 handler 还在等待处理消息, 从而导致了 Activity 不能及时的释放。引起内存的泄漏。
解决方式 :
(1) 静态修饰 handler ,保证 handler 不会持有 Activity , 不影响 Activity 的资源释放过程:
private static Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
(2) 弱引用 Activity ,当 jvm gc 的时候能够回收掉 Activity :
class MyHandler extends Handler {
WeakReference<Activity> mActivity;
public MyHandler(Activity con) {
this.mActivity = new WeakReference<Activity>(con);
}
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
Toast.makeText(Activity.this, "toast", 1000).show();
send();
}
};
}
4 线程引起的内存泄漏:
public class ThreadActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new MyThread() {
@Override
public void run() {
SystemClock.sleep(100000);
}
};
}
class MyThread extends Thread {
}
}
这里的线程案例和 handler 的内存泄漏原理类似,匿名内部类(或者非静态的内部类)会引用 Activity ,同时线程导致的阻塞,导致 Activity 不能及时释放资源。
解决方法:
(1) 静态修饰 MyThread 类,不再持有 Activity 。
(2) 当 Activity finish 的时候 ,对 线程进行 cancel 取消处理 。
5 WebView 引起的内存泄漏:
public class WebViewActivity extends Activity {
private WebView mWebView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = findViewById(R.id.web);
mWebView.loadUrl("[http://www.bug.com](http://www.bug.com/)");
}
@Override
protected void onDestroy() {
destoryWebView();
android.os.Process.killProcess(android.os.Process.myPid());
super.onDestroy();
}
private void destoryWebView() {
if (mWebView != null) {
mWebView.pauseTimers();
mWebView.removeAllViews();
mWebView.destroy();
}
}
}
android 的 webview 在加载网页的过程中会占用大量的堆内存,并且占用堆内存的空间和加载网页的数量成正比。上述代码已经写出了解决方案,我们不仅要释放 webview 的资源,同时最好把 webview 加载网页放置另外一个进程中,当加载网页的 activity 销毁的时候,同时将该进程杀死。避免占用过多堆内存引起的内存泄漏问题。
接下来,下篇文章分析 LeakCanary 如何搜集内存泄漏信息
android 使用 LeakCanary 分析内存泄漏原理(二)