如何处理:java.util.concurrent.TimeoutException:android.os.BinderProxy.finalize()在10秒后错误超时?

emm*_*mby 156 android garbage-collection

我们看到了一些TimeoutExceptionsin GcWatcher.finalize, BinderProxy.finalize,and PlainSocketImpl.finalize.其中90%以上发生在Android 4.3上.我们从现场用户那里获得了Crittercism的报告.

在此输入图像描述

该错误是以下变体:" com.android.internal.BinderInternal$GcWatcher.finalize() timed out after 10 seconds"

java.util.concurrent.TimeoutException: android.os.BinderProxy.finalize() timed out after 10 seconds
at 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)
Run Code Online (Sandbox Code Playgroud)

到目前为止,我们没有任何运气在内部复制问题或找出可能导致问题的原因.

有什么想法可以导致这个?知道如何调试这个并找出应用程序的哪个部分导致这个?任何能够揭示这个问题的东西都有帮助.

更多Stacktraces:

1   android.os.BinderProxy.destroy  
2   android.os.BinderProxy.finalize Binder.java, line 482
3   java.lang.Daemons$FinalizerDaemon.doFinalize    Daemons.java, line 187
4   java.lang.Daemons$FinalizerDaemon.run   Daemons.java, line 170
5   java.lang.Thread.run    Thread.java, line 841  
Run Code Online (Sandbox Code Playgroud)

2

1   java.lang.Object.wait   
2   java.lang.Object.wait   Object.java, line 401
3   java.lang.ref.ReferenceQueue.remove ReferenceQueue.java, line 102
4   java.lang.ref.ReferenceQueue.remove ReferenceQueue.java, line 73
5   java.lang.Daemons$FinalizerDaemon.run   Daemons.java, line 170
6   java.lang.Thread.run
Run Code Online (Sandbox Code Playgroud)

3

1   java.util.HashMap.newKeyIterator    HashMap.java, line 907
2   java.util.HashMap$KeySet.iterator   HashMap.java, line 913
3   java.util.HashSet.iterator  HashSet.java, line 161
4   java.util.concurrent.ThreadPoolExecutor.interruptIdleWorkers    ThreadPoolExecutor.java, line 755
5   java.util.concurrent.ThreadPoolExecutor.interruptIdleWorkers    ThreadPoolExecutor.java, line 778
6   java.util.concurrent.ThreadPoolExecutor.shutdown    ThreadPoolExecutor.java, line 1357
7   java.util.concurrent.ThreadPoolExecutor.finalize    ThreadPoolExecutor.java, line 1443
8   java.lang.Daemons$FinalizerDaemon.doFinalize    Daemons.java, line 187
9   java.lang.Daemons$FinalizerDaemon.run   Daemons.java, line 170
10  java.lang.Thread.run
Run Code Online (Sandbox Code Playgroud)

4

1   com.android.internal.os.BinderInternal$GcWatcher.finalize   BinderInternal.java, line 47
2   java.lang.Daemons$FinalizerDaemon.doFinalize    Daemons.java, line 187
3   java.lang.Daemons$FinalizerDaemon.run   Daemons.java, line 170
4   java.lang.Thread.run
Run Code Online (Sandbox Code Playgroud)

小智 200

完全披露 - 我是之前在TLV DroidCon中提到过的演讲的作者.

我有机会在许多Android应用程序中检查这个问题,并与遇到它的其他开发人员讨论它 - 我们都达到了同样的观点:这个问题无法避免,只能最小化.

我仔细研究了Android Garbage收集器代码的默认实现,以更好地理解为什么抛出此异常以及可能的原因.我甚至在实验过程中发现了可能的根本原因.

问题的根源是设备"进入休眠状态"一段时间 - 这意味着操作系统决定通过停止大部分用户域进程一段时间来降低电池消耗,并关闭屏幕,减少CPU周期这样做的方法是在Linux系统级别,其中进程在中期运行暂停.这可以在正常的应用程序执行期间的任何时间发生,但它将在Native系统调用时停止,因为上下文切换是在内核级别完成的.所以 - 这就是Dalvik GC加入故事的地方.Dalvik GC代码(在AOSP网站的Dalvik项目中实现)并不是一段复杂的代码.它的基本工作方式在我的DroidCon幻灯片中介绍.我没有涉及的是基本的GC循环 - 收集器有一个要完成的对象列表(和销毁).基础上的循环逻辑可以简化为:

  1. 拿起start_timestamp,
  2. 删除要释放的对象列表的对象,
  3. 释放对象 - 如果需要,starting_timestamp调用native finalize(),
  4. destroy(),
  5. calculate(end_timestamp)并与硬编码超时值10秒进行比较,
  6. 如果达到超时 - 抛出end_timestamp - starting_timestamp并终止进程.

现在考虑以下场景:

应用程序一直在运行.这不是面向用户的应用程序,它在后台运行.在此后台操作期间,将创建,使用对象并需要收集对象以释放内存.应用程序不会对Wakelock感到烦恼 - 因为这会对电池造成不利影响,而且似乎没有必要.这意味着应用程序将不时调用GC.通常情况下,GC运行完成没有任何障碍.有时(非常罕见)系统将决定在GC运行过程中休眠.如果您运行的应用程序足够长,并且密切监视Dalvik内存日志,则会发生这种情况.现在 - 考虑基本GC循环的时间戳逻辑 - 设备可以启动运行,获取start_stamp,然后java.util.concurrent.TimeoutException在系统对象的本机调用上进入休眠状态.当它醒来并恢复运行时,start_stamp将完成,下一个end_stamp将是destroy()通话所花费的时间+睡眠时间.如果睡眠时间很长 - 超过10秒,将抛出concurrent.timeout异常.

我已经在分析python脚本生成的图表中看到了这一点 - 对于Android系统应用程序,而不仅仅是我自己的受监控应用程序.收集足够的日志,你最终会看到它.

底线:

这个问题无法避免 - 如果你的应用在后台运行,你会遇到它.你可以通过唤醒唤醒来缓解,并防止设备睡觉,但这完全是一个不同的故事,一个新的头痛,也许是另一个骗局.

您可以通过减少GC调用来最小化问题 - 使方案不太可能.幻灯片中有提示.

我还没有机会浏览Dalvik 2(又名ART)GC代码 - 它拥有新的Generational Compacting功能,或者在Android Lollipop上进行任何实验.

2015年7月5日新增:

在查看此崩溃类型的崩溃报告聚合后,看起来这些来自Android OS 5.0版本(带有ART的Lollipop)的崩溃仅占此崩溃类型的0.5%.这意味着ART GC的改变降低了这些崩溃的频率.

2016年6月1日新增:

看起来Android项目已经添加了很多关于GC如何在Dalvik 2.0(又称ART)中工作的信息.你可以在这里阅读它 - 调试ART垃圾收集 .它还讨论了一些获取应用程序GC行为信息的工具.将SIGQUIT发送到您的应用程序进程实际上会导致ANR,并将应用程序状态转储到日志文件以进行分析.


kco*_*ock 67

我们使用Crashlytics在我们的应用程序中不断地看到这一点.崩溃通常发生在平台代码中.小样本:

android.database.CursorWindow.finalize()在10秒后超时

java.util.regex.Matcher.finalize()在10秒后超时

android.graphics.Bitmap $ BitmapFinalizer.finalize()在10秒后超时

org.apache.http.impl.conn.SingleClientConnManager.finalize()在10秒后超时

java.util.concurrent.ThreadPoolExecutor.finalize()在10秒后超时

android.os.BinderProxy.finalize()在10秒后超时

android.graphics.Path.finalize()在10秒后超时

发生这种情况的设备绝大多数(但不是唯一)由三星制造的设备.这可能意味着我们的大多数用户都在使用三星设备; 或者它可能表明三星设备有问题.我不太确定.

我想这并没有真正回答你的问题,但我只想强调这似乎很常见,并不是特定于你的应用程序.

  • 它也适用于Android 5.0.1版本,似乎并不局限于三星设备.它发生在Nexus 6上. (14认同)
  • 我在Android 4.4.4上有这个问题,设备由XIAOMI制造 (4认同)

Chr*_*her 14

我发现了一些有关此问题的幻灯片.

http://de.slideshare.net/DroidConTLV/android-crash-analysis-and-the-dalvik-garbage-collector-tools-and-tips

在这张幻灯片中,作者告诉我,如果堆中有很多对象或大对象,那么它似乎是GC的问题.幻灯片还包括对示例应用程序的引用和用于分析此问题的python脚本.

https://github.com/oba2cat3/GCTest

https://github.com/oba2cat3/logcat2memorygraph

此外,我在第3条评论中发现了一个提示:https://code.google.com/p/android/issues/detail?id = 53418#c3


小智 7

下面是滴滴解决这个问题的有效方案,由于这个bug很常见,很难找到原因,看起来更像是系统问题,为什么不能直接忽略呢?当然可以忽略,这里是示例代码:

final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = 
        Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        if (t.getName().equals("FinalizerWatchdogDaemon") && e instanceof TimeoutException) {
        } else {
            defaultUncaughtExceptionHandler.uncaughtException(t, e);
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

通过设置一个特殊的默认未捕获异常处理程序,应用程序可以改变那些已经接受系统提供的任何默认行为的线程处理未捕获异常的方式。当一个TimeoutException名为 的线程抛出一个未捕获的事件时FinalizerWatchdogDaemon,这个特殊的处理程序将阻塞处理程序链,系统处理程序不会被调用,因此将避免崩溃。

通过实践,没有发现其他不良影响。GC 系统仍在工作,随着 CPU 使用率的降低,超时得到缓解。

更多详情请见:https : //mp.weixin.qq.com/s/uFcFYO2GtWWiblotem2bGg


dan*_*117 5

10秒后广播接收器超时。可能是您从广播接收器进行了异步调用(错误),而4.3实际上检测到了它。

  • 似乎无法检测到并且没有告诉您足够的信息。让我们知道哪个广播会很好。 (3认同)

San*_*rma 5

有一件事总是正确的,那就是此时设备会因某些内存而窒息(这通常是 GC 最有可能被触发的原因)。

正如几乎所有作者之前提到的,当 Android 尝试在应用程序处于后台时运行 GC 时,此问题就会出现。在我们观察到的大多数情况下,用户通过锁定屏幕来暂停应用程序。这也可能表明应用程序中某处存在内存泄漏,或者设备已经加载过多。因此,最小化它的唯一合法方法是:

  • 确保没有内存泄漏,并且
  • 一般来说,减少应用程序的内存占用。


小智 5

我们通过停止来解决了这个问题FinalizerWatchdogDaemon

public static void fix() {
    try {
        Class clazz = Class.forName("java.lang.Daemons$FinalizerWatchdogDaemon");

        Method method = clazz.getSuperclass().getDeclaredMethod("stop");
        method.setAccessible(true);

        Field field = clazz.getDeclaredField("INSTANCE");
        field.setAccessible(true);

        method.invoke(field.get(null));

    }
    catch (Throwable e) {
        e.printStackTrace();
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以在Application的生命周期中调用方法,例如attachBaseContext()。出于同样的原因,您还可以指定手机的制造商来解决问题,这取决于您。