内存泄漏系列文章:
性能优化——内存泄漏(1)入门篇
性能优化——内存泄漏(2)工具分析篇
性能优化——内存泄漏(3)代码分析篇android
在上一篇《性能优化——内存泄漏(2)工具分析篇》中,介绍了如何使用工具帮助咱们检查APP中是否存在内存泄漏、及如何定位到内存泄漏,但项目并不能彻底依赖工具来检查,毕竟定位内存泄漏比较麻烦,还不如在开发时就考虑到内存泄漏问题,尽量减小内存泄漏,后续优化才不会那么痛苦。下面就来看看开发中,哪些代码可能形成内存泄漏,及避免内存泄漏的对应解决方案。性能优化
这个能够拿以前的Demo来讲明,Demo代码以下:网络
// 单例工具类
public class CommonUtil {
private static CommonUtil mInstance;
private Context mContext;
public CommonUtil(Context context) {
mContext = context;
}
public static CommonUtil getInstance(Context context) {
if (mInstance == null) {
synchronized (CommonUtil.class) {
if (mInstance == null) {
mInstance = new CommonUtil(context);
}
}
}
return mInstance;
}
...
}
// Activity中使用单例工具
public class MemoryLeakActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memory_leak);
CommonUtil.getInstance(this);
}
}复制代码
当调用getInstance()时,若是传入的context是Activity,那么只要这个单例没有被释放,则这个Activity也不会被释放,直到进程退出后才会释放。app
不要传入Activity,能够使用getApplicationContext()来代替。ide
在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。函数
public class MemoryLeakActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memory_leak);
loadData();
}
public void loadData() {
new Thread() {
@Override
public void run() {
try {
Thread.sleep(10000);
System.out.println("模拟同步网络数据完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}复制代码
上面的代码中,使用Thread匿名内部类开辟线程同步网络数据,而这个内部类会隐式持有外部类的引用,当退出界面后,该内部类任务还在进行,致使该界面没法被GC回收,因而就会产生内存泄漏。工具
APP退出后,执行GC,获取内存快照。能够看到MemoryLeakActivity的Total Count为1,说明存在内存泄漏。post
静态的内部类不会持有外部类的引用。性能
public static void loadData() {
new Thread() {
@Override
public void run() {
try {
Thread.sleep(10000);
System.out.println("模拟同步网络数据完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}复制代码
public void loadData() {
new MyThread().start();
}
static class MyThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(10000);
System.out.println("模拟同步网络数据完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}复制代码
但项目开发中,可能会存在必定要使用内部类去持有外部类的状况,好比数据同步完成后,须要修改界面上的文本信息,而静态内部类没法直接持有外部类的引用,这又该怎么解决呢?其实能够经过内部类的构造函数将外部类传入,并使用弱引用保存(GC执行后释放),并在内部类中作好判空便可。优化
static class MyThread extends Thread { ReferencemReference; public MyThread(Context context) { mReference = new WeakReference<>(context); } @Override public void run() { try { Thread.sleep(10000); MemoryLeakActivity context = (MemoryLeakActivity) mReference.get(); if (context != null) { context.mTv.setText("模拟同步网络数据完毕"); } } catch (InterruptedException e) { e.printStackTrace(); } } } 复制代码
APP退出后,执行GC,获取内存快照。能够看到MemoryLeakActivity的Total Count为0,说明没有内存泄漏。
public class MemoryLeakActivity extends AppCompatActivity implements SensorEventListener {
private SensorManager mSm;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memory_leak);
mSm = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensor = mSm.getDefaultSensor(Sensor.TYPE_ALL);
mSm.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}复制代码
本例中使用getSystemService()获取传感器服务,并设置了监听,但没有在onDestroy()方法中将监听移除,此类监听没有及时清除的话,一定形成内存泄漏。
只需在onDestroy()中移除监听便可。
@Override
protected void onDestroy() {
super.onDestroy();
mSm.unregisterListener(this);
}复制代码
好比:BroadCastReceiver、Cursor、Bitmap、IO流、自定义属性。
当不须要使用的时候,要记得及时释放资源。不然就会内存泄露。
这里以Cursor、IO流和自定义属性为例。
try {
...
使用cursor/io读取数据操做
...
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor/io != null) {
cursor/io.close();
cursor/io = null;
}
}复制代码
TypedArray a = theme.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.TextViewAppearance, defStyleAttr, defStyleRes);
TypedArray appearance = null;
int ap = a.getResourceId(
com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1);
a.recycle();// 不执行回收会形成内存泄漏复制代码