Chinaunix首页 | 论坛 | 博客
  • 博客访问: 123200
  • 博文数量: 29
  • 博客积分: 652
  • 博客等级: 上士
  • 技术积分: 340
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-03 21:26
文章分类

全部博文(29)

文章存档

2013年(8)

2012年(21)

分类: 嵌入式

2012-07-09 09:30:06

打开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:

点击(此处)折叠或打开

  1. public class PhoneStateListener {
  2. ...
  3. IPhoneStateListener callback = new IPhoneStateListener.Stub() {
  4. ...
  5. }
  6. }
查看RadioInfo.java, RadioInfo
肯定调用了API释放PhoneStateListener,但为什么Hprof还显示PhoneStateListener$1?

注意到callback是binder的服务类。RadioInfo把这个callback注册到。RadioInfo释放PhoneStateListner就是在
中注销PhoneStateListner,会不会没有成功注销 PhoneStateListner? 由于binder的客户端(中的注册PhoneStateListner)没有释放,导致服务端以为仍有一个客户,不能释放。

最后在中发现,PhoneStateListner注册时调用了下面的代码:

点击(此处)折叠或打开

  1. try {
  2.    r.recipient = new DeathRecipient(b);
  3.    b.linkToDeath(r.recipient, 0);
  4.  } catch (RemoteException ex) {
  5.    remove(r.binder);
  6.  }
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中。
阅读(4051) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~