CSDN话题挑战赛第2期
参赛话题:面试宝典
1. 内部类
public class MyClass2 {
public void out(){
new Thread(){
@Override
public void run() {
}
};
}
}
/**
编译后会产生两个class文件:MyClass2.class、MyClass2$1.class
*/
// MyClass2$1.class
class MyClass2$1 extends Thread {
MyClass2$1(MyClass2 var1) {
this.this$0 = var1; // 这个this$0就是隐式的引用外部类引用
}
public void run() {
}
}
现象
- 为什么非静态内部类对外部类会存在一个隐式引用?
- 内部类可以引用外部类的方法,因为省略了外部类.this,所以才会说非静态内部类对外部类会存在一个隐式引用
- 非静态内部类和非静态匿名内部类中确实都持有外部类的引用, 静态内部类中未持有外部类的引用。
解决方案
- 去除隐式引用(通过静态内部类来去除隐式引用)
- 手动管理对象引用(修改静态内部类的构造方式,手动引入其外部类引用)
- 当内存不可用时,不执行不可控代码(Android可以结合智能指针,WeakReference包裹外部类实例)
2. Service
如果通过 bindService
方式进行开启 Service,退出相应 Activity 后,没有 unbindService
,则会有内存泄漏;
绑定的方式,通过ServiceConnection
的方式进行绑定,如果没有解绑的话,Activity则会内存泄漏
private val conn = object : ServiceConnection{
override fun onServiceConnected(name:ComponentName, service: IBinder) {
LogUtil.v(simpleName, "service:$service")
//连接后拿到 Binder,转换成 AIDL,在不同进程会返回个代理
iPersonInterface = ManualBinder.asInterface(service)
// IAidlInterface iAidlInterface = AIDLBinder.asInterface(service);
try {
service.linkToDeath(mDeathRecipient, 0)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
override fun onServiceDisconnected(name: ComponentName) {
iPersonInterface = null
}
}
以及Service
自身由于Binder的原因,Activity内存泄漏,持有Binder也不会被回收,从而也会造成Service自身内存泄漏
3. BroadcastReceiver
动态注册的广播,如果退出相应 Activity 后没有 unresiterReceiver,则会有内存泄漏;如果Receiver
中有延迟操作,未能及时取消注册广播,也会造成Activity内存泄漏,进而造成Recevier内存泄漏
4. 线程泄漏
run
方法中持有的外部类引用或关联对象,在它们需要退出时,未及时退出run
方法,从而造成线程持有的相关引用未能及时释放,引发内存泄漏
5. 单例泄漏
由于单例类是一个单例模式,那么这个类的生命周期就伴随整个应用的生命周期,而它在被单例类创建的时候引用了Activity,所以当系统GC的时候试图去回收Activity时,发现它却在被另一个仍然在内存里的单例类所引用,所以GC回收它失败,从而导致了内存泄漏。
解决方案:对上述的 Activity(Context)属性使用弱引用即可;
6. 系统泄漏
InputMethodManager自身也会存在内存泄漏,解决方式如下
InputMethodManager im = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
try {
//获得 所有属性 getField->public 以及父类
Field mCurRootViewField = InputMethodManager.class.getDeclaredField("mCurRootView");
//设置允许访问权限
mCurRootViewField.setAccessible(true);
// 指定对象上此字段表示的值
Object mCurRootView = mCurRootViewField.get(im);
if (null != mCurRootView){
Context context = ((View) mCurRootView).getContext();
if (context == this){
//破怪gc 引用链
mCurRootViewField.set(im,null);
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}