如何处理: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()
并在需要时调用nativedestroy()
取结束时间戳
计算(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();
}
}