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

如何处理:java.util.concurrent.TimeoutException:android.os.BinderProxy.finalize()在出现10秒错误后超时?,android,garbage-collection,Android,Garbage Collection,我们在GcWatcher.finalize、BinderProxy.finalize和PlainSocketImpl.finalize中看到许多TimeoutExceptions。其中90%以上发生在Android 4.3上。我们从Critercism收到了来自现场用户的报告 错误是“com.android.internal.BinderInternal$GcWatcher.finalize()在10秒后超时” 到目前为止,我们还没有在家里重现这个问题,也没有找到可能的原因 你知道这是什么原因

我们在
GcWatcher.finalize、BinderProxy.finalize
PlainSocketImpl.finalize
中看到许多
TimeoutExceptions
。其中90%以上发生在Android 4.3上。我们从Critercism收到了来自现场用户的报告

错误是“
com.android.internal.BinderInternal$GcWatcher.finalize()在10秒后超时”

到目前为止,我们还没有在家里重现这个问题,也没有找到可能的原因

你知道这是什么原因吗? 你知道如何调试并找出应用程序的哪个部分会导致这种情况吗? 任何能揭示这一问题的东西都会有所帮助

更多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  
二,

三,

四,


我们经常在应用程序中看到这种情况,使用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秒后超时

发生这种情况的设备绝大多数(但不完全)是三星制造的设备。这可能仅仅意味着我们的大多数用户都在使用三星设备;或者,这可能表明三星设备存在问题。我不太确定


我想这并不能真正回答您的问题,但我只是想强调一点,这似乎很常见,并不特定于您的应用程序。

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

我找到了一些关于这个问题的幻灯片

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


此外,我在评论#3中发现了一个提示:

完全披露-我是TLV DroidCon中前面提到的演讲的作者

我曾有机会在许多Android应用程序中研究过这个问题,并与遇到这个问题的其他开发人员进行了讨论——我们都得出了相同的观点:这个问题无法避免,只能最小化

我仔细研究了Android垃圾收集器代码的默认实现,以便更好地理解引发此异常的原因以及可能的原因。我甚至在实验中找到了可能的根本原因

问题的根源在于设备“进入睡眠”一段时间——这意味着操作系统决定通过停止大多数用户登录进程一段时间、关闭屏幕、减少CPU周期等方式来降低电池消耗。这样做的方式是在Linux系统级别上,进程在运行中暂停。这可以在正常应用程序执行期间的任何时候发生,但会在本机系统调用时停止,因为上下文切换是在内核级别完成的。这就是Dalvik GC加入故事的地方

Dalvik GC代码(在AOSP站点的Dalvik项目中实现)不是一段复杂的代码。我的DroidCon幻灯片介绍了它的基本工作方式。我没有介绍的是基本的GC循环——收集器有一个要完成(和销毁)的对象列表。底部的循环逻辑可以简化如下:

  • 开始时间戳
  • 移除要释放的对象列表的对象
  • 释放对象-
    finalize()
    并在需要时调用native
    destroy()
  • 结束时间戳
  • 计算(
    end\u timestamp-start\u timestamp
    )并与10秒的硬编码超时值进行比较
  • 如果超时已达到-抛出
    java.util.concurrent.TimeoutException
    并终止进程
  • 现在考虑下面的场景: 应用程序一直在做自己的事情

    这不是面向用户的应用程序,它在后台运行

    在此后台操作期间,将创建、使用对象,并需要收集对象以释放内存

    应用程序不需要WakeLock,因为这会对电池产生不利影响,而且似乎没有必要

    这意味着应用程序将不时调用GC

    一般来说,GC的运行是顺利完成的

    有时(很少)系统会决定在GC运行过程中睡觉。

    如果您运行应用程序的时间足够长,并且密切监视Dalvik内存日志,就会发生这种情况

    现在考虑基本GC循环的时间戳逻辑-设备可以启动运行,取一个<代码> StasyStope<代码>,然后在系统代码对象的<代码>销毁()/代码>本地调用中睡觉。< /P> 当它唤醒并恢复运行时,

    destroy()
    将完成,下一个
    end\u标记将是调用
    destroy()
    所用的时间+睡眠时间

    如果睡眠时间很长(超过10秒),将抛出
    java.util.concurrent.TimeoutException

    我在分析python脚本生成的图表中看到了这一点——针对Android系统应用程序,而不仅仅是我自己监控的应用程序

    收集足够的日志,你最终会看到它

    底线: 这个问题无法避免-
    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  
    
    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
    
    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
    
    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
    
    try {
        Class<?> c = Class.forName("java.lang.Daemons");
        Field maxField = c.getDeclaredField("MAX_FINALIZE_NANOS");
        maxField.setAccessible(true);
        maxField.set(null, Long.MAX_VALUE);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    
    private static class MyCur extends MatrixCursor {
    
    
        public MyCur(String[] columnNames) {
            super(columnNames);
        }
    
        @Override
        protected void finalize() {
            super.finalize();
    
            try {
                for (int i = 0; i < 1000; i++)
                    Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    for (int i = 0; i < 7; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    MyCur cur = null;
                    try {
                        cur = new MyCur(new String[]{});
                        longRun();
                    } finally {
                        cur.close();
                    }
                }
    
                private void longRun() {
                    try {
                        for (int i = 0; i < 1000; i++)
                            Thread.sleep(30);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    
    FATAL EXCEPTION: FinalizerWatchdogDaemon
                                                                            Process: la.la.land, PID: 29206
                                                                            java.util.concurrent.TimeoutException: MyCur.finalize() timed out after 10 seconds
                                                                                at java.lang.Thread.sleep(Native Method)
                                                                                at java.lang.Thread.sleep(Thread.java:371)
                                                                                at java.lang.Thread.sleep(Thread.java:313)
                                                                                at MyCur.finalize(MessageList.java:1791)
                                                                                at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:222)
                                                                                at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:209)
                                                                                at java.lang.Thread.run(Thread.java:762)
    
    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();
        }
    }