美文网首页
TimeoutException: View.finalize(

TimeoutException: View.finalize(

作者: 已迁移 | 来源:发表于2017-04-24 17:30 被阅读406次

一、原因

查找资料后发现,该异常在5.0以上出现频率最高,其原因在ART虚拟机的垃圾回收机制中。

ART虚拟机垃圾回收共有5个相关守护线程:

public final class Daemons {
    private static final int NANOS_PER_MILLI = 1000 * 1000;
    private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
    private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND;
 
    public static void start() {
        ReferenceQueueDaemon.INSTANCE.start();
        FinalizerDaemon.INSTANCE.start();
        FinalizerWatchdogDaemon.INSTANCE.start();
        HeapTrimmerDaemon.INSTANCE.start();
        GCDaemon.INSTANCE.start();
    }
}

问题出在 FinalizerWatchdogDaemon 这个守护线程中,在GC时,复写了 finalize() 方法的对象并不会立刻被回收,而是被放入队列依次执行 finalize() 方法,FinalizerWatchdogDaemon 这个守护线程就是去监视 finalize() 方法是否耗时,如果超过了最大阈值,那么抛出异常。而在锁屏后系统进入休眠模式,执行方法时间很容易超过超时阈值。

抛出异常的代码在此:

private static void finalizerTimedOut(Object object) {
    // The current object has exceeded the finalization deadline; abort!
    String message = object.getClass().getName() + ".finalize() timed out after "
            + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds";
    Exception syntheticException = new TimeoutException(message);
    // We use the stack from where finalize() was running to show where it was stuck.
    syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
    Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();
    if (h == null) {
        // If we have no handler, log and exit.
        System.logE(message, syntheticException);
        System.exit(2);
    }
    // Otherwise call the handler to do crash reporting.
    // We don't just throw because we're not the thread that
    // timed out; we're the thread that detected it.
    h.uncaughtException(Thread.currentThread(), syntheticException);
}

可以看到,阈值由 MAX_FINALIZE_NANOS / NANOS_PER_SECOND 计算出来,正好是 10 。

二、修复

在锁屏后,我们可以通过反射修改 Daemons 类的 MAX_FINALIZE_NANOS 值,在开屏后修改回来。

本机验证后通过,代码如下:

try {
    Class<?> c = Class.forName("java.lang.Daemons");
    Field maxField = c.getDeclaredField("MAX_FINALIZE_NANOS");
    maxField.setAccessible(true);
    long oldValue = (long) maxField.get(null);
    maxField.set(null, Long.MAX_VALUE);
 
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (NoSuchFieldException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}

由于 MAX_FINALIZE_NANOS 本身需要经过计算才能获得结果(10L * NANOS_PER_SECOND),所以系统在调用时并不会受Java 虚拟机内联的影响,从而正确调用到我们修改后的值。

相关文章

网友评论

      本文标题:TimeoutException: View.finalize(

      本文链接:https://www.haomeiwen.com/subject/jsvpzttx.html