一、什么是内存泄漏?
- 无用的对象没有被及时释放引用,导致GC无法回收,就有可能出现内存泄漏。
二、内存泄漏带来的危害是什么?
- 内存泄漏会增加内存占用和OOM几率。
三、哪些代码的写法会导致内存泄漏?
- 常见五种导致 APP 内存泄漏的地方
- 静态 Activity
泄漏 activity 最简单的方法就是在 activity 类中定义一个 static 变量,并且将其指向一个运行中的 activity 实例。如果在 activity 的生命周期结束之前,没有清除这个引用,那它就会泄漏了。这是因为 activity(例如 MainActivity) 的类对象是静态的,一旦加载,就会在 APP 运行时一直常驻内存,因此如果类对象不卸载,其静态成员就不会被垃圾回收。
void setStaticActivity() {
activity = this;
}
View saButton = findViewById(R.id.sa_button);
saButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
setStaticActivity();
nextActivity();
}
});
2. 内部类或者匿名内部类
类似的,匿名类同样会持有定义它们的对象的引用。因此如果在 activity 内定义了一个匿名的 AsyncTask 对象,就有可能发生内存泄漏了。如果 activity 被销毁之后 AsyncTask 仍然在执行,那就会组织垃圾回收器回收 activity 对象,进而导致内存泄漏,直到执行结束才能回收 activity。
void startAsyncTask() {
new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... params) {
while(true);
}
}.execute();
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View aicButton = findViewById(R.id.at_button);
aicButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
startAsyncTask();
nextActivity();
}
});
3. Handlers
同样的,定义一个匿名的 Runnable 对象并将其提交到 Handler 上也可能导致 activity 泄漏。Runnable 对象间接地引用了定义它的 activity 对象,而它会被提交到 Handler 的 MessageQueue 中,如果它在 activity 销毁时还没有被处理,那就会导致 activity 泄漏了。
void createHandler() {
new Handler() {
@Override public void handleMessage(Message message) {
super.handleMessage(message);
}
}.postDelayed(new Runnable() {
@Override public void run() {
while(true);
}
}, Long.MAX_VALUE >> 1);
}
View hButton = findViewById(R.id.h_button);
hButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
createHandler();
nextActivity();
}
});
4. EditText
EditText所处的上下文对象会一直保留在(4.0.x,4.1.x,4.2.x)的系统版本中。大家在用着它这么长的时间不知道系统还有这么个bug(具体可参考:https://dddpaul.github.io/blog/2014/08/01/android-memory-leaks/),但好在Android 4.3, API 18的系统版本上修复了该问题,但是对于18以下的就要注意啦,需要做些特殊处理,不然就会导致泄漏的,其实解决办法也是很简单:修改EditText的android:inputType= “textNoSuggestions”
泄露的具体信息如图:
- Threads
同样的,使用 Thread 和 TimerTask 也可能导致 activity 泄漏。
void spawnThread() {
new Thread() {
@Override public void run() {
while(true);
}
}.start();
}
View tButton = findViewById(R.id.t_button);
tButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
spawnThread();
nextActivity();
}
});