打开Hprof,查看dominator_tree:
RadioInfo可能有leak. 右键点击"com.android.settings.RadioInfo"==>"path to GC root"==>"Exclude wek/soft references"得到hold这个reference的根节点:
根节点在PhoneStateListener$1,$1表示是PhoneStateListener的一个内嵌类,这个内嵌类的reference在native Stack里。
怀疑$1是PhoneStateListener里的内嵌类IPhoneStateListener callback:- public class PhoneStateListener {
- ...
- IPhoneStateListener callback = new IPhoneStateListener.Stub() {
- ...
- }
- }
查看RadioInfo.java, RadioInfo肯定调用了API释放PhoneStateListener,但为什么Hprof还显示PhoneStateListener$1?
注意到callback是binder的服务类。RadioInfo把这个callback注册到。RadioInfo释放PhoneStateListner就是在中注销PhoneStateListner,会不会没有成功注销 PhoneStateListner? 由于binder的客户端(中的注册PhoneStateListner)没有释放,导致服务端以为仍有一个客户,不能释放。
最后在中发现,PhoneStateListner注册时调用了下面的代码:
- try {
- r.recipient = new DeathRecipient(b);
- b.linkToDeath(r.recipient, 0);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
DeathRecipient是中的一个内嵌类,b是PhoneStateListner.callback的BpBinder类,line2使deathReceipt保留了PhoneStateListner.callback的reference,然后line3又通过linkToDeath()把这个reference保存到一个vector,如果在释放时忘记(),那这个reference就一直在vector中,导致BpBinder无法释放。
事实上,情况就是这样。
总结:
1)一般OOM错误只要关心OOM所在进程就可以了。但这个例子说明memory leak有可能是另外一个进程,它通过binder影响对端进程。这一点可以查看:
void ::(const void* ){
... if () ->();
}
2) 非静态内嵌类都有一个暗含的外部类this引用,这个往往导致无意识引用一个大类。3) 刚开始看这个问题时,发现hprof中有大量的FinalizeReference,以为是FinalizeDaemon没有及时释放FinalizeReference,导致OOM。其实,只要类带有finalizer()方法,就是一个FinalizeReference。GC发现这个类无其他类引用时,会把它的zombie设为这个类的Reference,并放入FinailizeQueue中。
阅读(4042) | 评论(0) | 转发(0) |