Android中的BinderProxy.finalize()或PlainSocketImpl.finalize()导致的TimeoutExceptions

翻译:
http://stackoverflow.com/questions/24021609/how-to-handle-java-util-concurrent-timeoutexception-android-os-binderproxy-fin

有时会遇到以下崩溃:

java.util.concurrent.TimeoutException: java.net.PlainSocketImpl.finalize() timed out after 10 secondslibcore.io.Posix.close(Native Method)libcore.io.BlockGuardOs.close(BlockGuardOs.java:75)libcore.io.IoBridge.closeSocket(IoBridge.java:188)java.net.PlainSocketImpl.close(PlainSocketImpl.java:162)java.net.PlainSocketImpl.finalize(PlainSocketImpl.java:210)java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:187)java.lang.Daemons$FinalizerDaemon.run(Daemons.java:170)java.lang.Thread.run(Thread.java:841)

The error is a variation of: "com.android.internal.BinderInternal$GcWatcher.finalize() timed out after 10 seconds"java.util.concurrent.TimeoutException: android.os.BinderProxy.finalize() timed out after 10 secondsat android.os.BinderProxy.destroy(Native Method)at android.os.BinderProxy.finalize(Binder.java:459)at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:187)at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:170)at java.lang.Thread.run(Thread.java:841)

它其实是发生在 GcWatcher.finalize, BinderProxy.finalize 和 PlainSocketImpl.finalize 中的一类TimeoutExceptions。这个异常90%都是发生在4.3、4.4的android系统上。

这个问题的根源在于设备会’Goes to Sleep’一会儿,就是说操作系统会通过熄屏、降低cpu循环等方式降低电量消耗,进入休眠状态。它是通过在内核层暂停进程的方式来实现的。这可能发生在常规app运行的过程中, 但是会停在一次内核调用上,比如内核层的上下文切换。这就是Dalvik GC参与最初所说TimeoutExceptions问题的方式。

Dalvik GC的基本工作方式就是,在GC循环中将收集到的一系列的对象去销毁,其主要流程可以描述为:

  • take starting_timestamp,
  • remove object for list of objects to release,
  • release object - finalize() and call native destroy() if required,
  • take end_timestamp,
  • calculate (end_timestamp - starting_timestamp) and compare against a hard coded timeout value of 10 seconds,
  • if timeout has reached - throw the concurrent.TimeoutException and kill the process.

现在思考下接下来的这个场景:
有一个后台运行的进程,在运行过程中,对象被创建、使用以及被收集(以释放内存)。一般的,应用不会使用Wakelock,因为会很耗电并且也没必要,这意味着应用会不时的执行GC动作。通常情况下,GC动作会正常的执行完成而不会被挂起。但是,有些时候(很稀少),操作系统会在GC动作的过程中进入休眠。如果你的应用运行时间足够长,它就有可能发生。现在,想一下GC循环中的有关时间戳的逻辑:有可能发生,设备开始进行GC,并且在处理系统对象销毁(native层的destroy())的过程中进入休眠, 然后被唤醒,恢复运行,记录现在的时间戳,也就是说这次GC动作花费的时间=销毁动作执行时长+休眠时长。如果休眠时间超过10s, 就会抛出concurrent.timeout异常。

这个问题不能完全避免,只要你的应用在后台运行。我们可以通过调用wakelock减少设备休眠,但这个方法会引来一系列问题。最靠谱的方法还是,* 尽量减少GC动作的被调用次数 *,使得这个场景少出现。

另外,在Android5.0+系统上,因为使用了ART GC,使得这个崩溃的发生机率大大降低了。
目前Android project增加了大量的关于’GC是如何(在ART上)工作’ 的之类的文档,感兴趣的话,可以看看:https://source.android.com/devices/tech/dalvik/gc-debug.html

转自: https://www.jianshu.com/p/0119c682d2b8

更多相关文章

  1. Android--MediaPlayer音乐播放器歌词随时间高亮显示
  2. android触摸实现方式
  3. ConstraintLayout 使用可视化的方式来编写Android应用程序的界面
  4. Android添加快捷方式(Shortcut)到手机桌面
  5. android两种方式设置EditText不弹出软键盘
  6. Button、选择框、日期、时间控件
  7. android listview继承BaseAdapter,自定义的适配器,getView方法执
  8. Android Activity四种启动方式

随机推荐

  1. 【北亚服务器数据恢复】raid5硬盘掉线,阵
  2. 初学者怎么把姿势画好?动漫人物少女姿势画
  3. Python多线程编程
  4. Linux 中如何使用 Htop 监控工具?【网络安
  5. Ement语法,元素属性及布局标签
  6. markdown语法练习
  7. markdown的基本操作
  8. markdown和html的基本认识
  9. cg插画怎么学?cg插画入门基础知识
  10. Python "爬虫"出发前的装备之简单实用的