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 studio 运行程序提示Application installation failed
  2. android休眠运行
  3. Windows环境下搭建Cocos2dx的android开发环境
  4. android studio 上junit4的使用
  5. android休眠与唤醒驱动流程分析
  6. Android(安卓)activity属性设置大全
  7. android的init.rc文件的语法
  8. Android(安卓)Activity 属性介绍
  9. Meteor 的 Android(安卓)支持

随机推荐

  1. adb如何连接mumu模拟器并修改Android ID
  2. Android支持播放网络上的视频
  3. android edittext只能输入字母和数字,默认
  4. Android :java.lang.RuntimeException: An
  5. android 反编译apk文件
  6. andoid HorizontalScrollView 水平滚动问
  7. android 获取wifi mac 地址的代码
  8. android Button控件的四种不同方法
  9. android GridView 的使用
  10. Android中RecyclerView简单的点击事件