Як поводитися: java.util.concurrent.TimeoutException: android.os.BinderProxy.finalize () вичерпано після 10-секундних помилок?


167

Ми бачимо кількість TimeoutExceptionsв. GcWatcher.finalize, BinderProxy.finalizeТа 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)

Поки нам не пощастило відтворити проблему в будинку або з'ясувати, що це могло викликати.

Будь-які ідеї, що може викликати це? Будь-яка ідея, як налагодити це та дізнатися, яка частина програми викликає це? Все, що проливає світло на питання, допомагає.

Більше стектейсів:

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  

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

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

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

2
Неважливо, знайшли це bugzilla.mozilla.org/show_bug.cgi?id=864102 Я також можу підтвердити, що впливає на наші додатки, це пахне проблемою
сервісів

Рядок коду, який викидається з помилки, був введений Версія 4.3_r1, яка була випущена 5 червня 2013 року. Можливо, проблема виникає з того часу.
edubriguenti

Android версії 4.2.2 також почала викидати цей виняток, тож можливо його оновлення в програванні Google, яке є джерелом.
JWqvist

@EvelioTarazona Я маю це в якомусь додатку, який не використовує ігрові сервіси
ліга

@ligi це той самий стек-слід для вас?
eveliotc

Відповіді:


220

Повне розкриття - я автор згаданої раніше бесіди в TLV DroidCon.

У мене була можливість вивчити цю проблему в багатьох додатках Android і обговорити її з іншими розробниками, які зіткнулися з нею - і ми всі дійшли до одного і того ж: цього питання не уникнути, а лише мінімізувати.

Я детальніше ознайомився з реалізацією за замовчуванням коду колектора Android Garbage, щоб зрозуміти, чому саме цей виняток кидається та про те, які можуть бути можливі причини. Я навіть знайшов можливу першопричину під час експериментів.

Корінь проблеми полягає в тому, що на деякий час пристрій «переходить у сон» - це означає, що ОС вирішила знизити витрату акумулятора, зупинивши на деякий час більшість процесів User Land та вимкнувши екран, скорочуючи цикли процесора і т. д. Це робиться - це на системному рівні Linux, де процеси призупинені в середньому циклі. Це може статися в будь-який час під час звичайного виконання програми, але воно припиниться при виклику Native системи, оскільки переключення контексту здійснюється на рівні ядра. Отже - саме тут до історії приєднується Далвік GC.

Код GC Dalvik (як реалізовано в проекті Dalvik на сайті AOSP) не є складним кодом. Основний спосіб його роботи висвітлений у моїх слайдах DroidCon. Те, що я не охоплював, - це базовий цикл GC - у тому місці, коли колектор має перелік Об'єктів для завершення (та знищення). Логіку циклу в основі можна спростити так:

  1. взяти starting_timestamp,
  2. видалити об’єкт для списку об'єктів, які потрібно випустити,
  3. випустити об'єкт - finalize()і за потреби зателефонувати до нативного destroy(),
  4. взяти end_timestamp,
  5. обчислити ( end_timestamp - starting_timestamp) і порівняти з жорстко кодованим значенням тайм-ауту 10 секунд,
  6. якщо тайм-аут досяг, киньте процес java.util.concurrent.TimeoutExceptionі вбийте його.

Тепер розглянемо наступний сценарій:

Додаток працює разом, роблячи свою справу.

Це не програма, що стоїть перед користувачем, вона працює у фоновому режимі.

Під час цієї фонової операції об’єкти створюються, використовуються та їх потрібно збирати для звільнення пам'яті.

Програма не турбує WakeLock - оскільки це вплине на акумулятор негативно, і здасться непотрібним.

Це означає, що Додаток час від часу буде викликати GC.

Зазвичай запуск GC виконується без зачеплення.

Іноді (дуже рідко) система вирішить спати в середині пробігу GC.

Це станеться, якщо ви будете запускати свою програму досить довго та уважно стежити за журналами пам’яті Dalvik.

Тепер - розглянемо логіку часових позначок основного циклу GC - пристрій може запустити пробіг, взяти a start_stampі перейти до сну при destroy()нативному виклику системного об'єкта.

Коли він прокинеться і відновить біг, destroy()завершиться, а наступним end_stampбуде час, який йому потрібно було destroy()дзвонити + час сну.

Якщо час сну був довгим (більше 10 секунд), java.util.concurrent.TimeoutExceptionволя буде кинута.

Я бачив це на графіках, створених за допомогою сценарію python для аналізу - для системних програм Android, а не лише для моїх власних програм, що відстежуються.

Зберіть достатньо журналів, і ви зрештою його побачите.

Нижня лінія:

Проблему не уникнути - ви зіткнетеся з нею, якщо ваша програма працює у фоновому режимі.

Можна пом'якшити, взявши WakeLock, і запобігти спанню пристрою, але це зовсім інша історія, і новий головний біль, а може бути, ще одна розмова в іншому кон.

Ви можете мінімізувати проблему, зменшивши виклики GC - зробивши сценарій менш вірогідним (поради є у слайдах).

У мене ще не було можливості перейти GC код Dalvik 2 (він же ART) - який може похвалитися новою функцією Generational Compacting або здійснив будь-які експерименти на Android Lollipop.

Додано 5.05.2015:

Переглянувши агрегацію звітів про збої для цього типу збоїв, схоже, що на ці збої з версії 5.0+ ОС Android (Lollipop з ART) припадає лише 0,5% цього типу збоїв. Це означає, що зміни ART GC зменшили частоту цих збоїв.

Додано 01.06.2016:

Схоже, проект Android додав багато інформації про те, як працює GC у Dalvik 2.0 (він же ART).

Про це можна прочитати тут - Налагодження колекції сміття ART .

Тут також обговорюються деякі інструменти для отримання інформації про поведінку GC для вашої програми.

Надсилання SIGQUIT до вашої програми додатків по суті спричинить ANR та скидає стан програми у файл журналу для аналізу.


У моєму випадку я також планую спробувати пом’якшити це, знайшовши способи зменшити кількість коду / часу, який я запускаю у фоновому режимі. Дякуємо за ваше дослідження по цій темі.
parkerfath

видалення фонової обробки у вашій програмі значно допоможе зменшити проблему.
оба

Що варто, це все ще трапляється на Зефірі (6.0.1). Це означає, що я лише один раз отримував цю помилку. Тож це не здається велетенською проблемою. Дякую за ретельне пояснення.
Knossos

Через деякий час у мене склалося чітке враження, що виправити цю проблему в ОС дуже проблематично і вимагає співпраці між Google та виробниками виробника. Я не очікую, що це скоро виправиться.
Оба

Я використовую будинок, але все ще стикався з цією проблемою на Android 4.4.2. У моєму додатку є деякі фонові операції, але в основному вони розроблені для роботи протягом усього дня, поки заряджений кабель. Чи є якийсь інший спосіб пом'якшити це питання?
Оркун Севсей

74

Ми спостерігаємо це постійно, у всьому нашому додатку, використовуючи 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 секунд

Пристрої, на яких це відбувається, є переважною (але не виключно) пристроями виробництва Samsung. Це може означати, що більшість наших користувачів використовують пристрої Samsung; по черзі це може означати проблему з пристроями Samsung. Я не дуже впевнений.

Я вважаю, що це насправді не відповідає вашим питанням, але я просто хотів підкреслити, що це здається досить поширеним і не стосується вашої заявки.


16
Це відбувається також для версії Android 5.0.1 і, здається, не обмежується лише пристроями Samsung. Це сталося на Nexus 6.
Shobhit Puri

4
У мене ця проблема на android 4.4.4 з пристроєм виробництва XIAOMI
Paresh Dudhat

Просто хотілося зазвучити в тому, що ми бачимо більшість цих збоїв на планшетах Samsung. Не впевнений, що Samsung зробив інакше, як планшети обробляють фонові програми.
FriendlyMikhail

1
У мене це питання на Android 4.4.4. пристрій виробництва HUAWEI.
Рамешбабу

1
Мій додаток виходить із ладу після того, як я використовую бібліотеку витоків канарок на пристрої Samsung Android 5.0.2. Якщо я відключу ініціалізацію бібліотеки, програма працює чудово.
ваномарт

15

Я знайшов слайди з цього питання.

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

У цьому слайді автор розповідає, що, здається, це проблема з GC, якщо в купі багато об’єктів чи величезних об'єктів. Слайд також містить посилання на зразок програми та сценарій пітона для аналізу цієї проблеми.

https://github.com/oba2cat3/GCTest

https://github.com/oba2cat3/logcat2memorygraph

Крім того, я знайшов підказку в коментарі №3 з цієї сторони: https://code.google.com/p/android/isissue/detail?id=53418#c3


7

Ми вирішили проблему, зупинивши 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();
    }
}

Ви можете викликати метод у життєвому циклі програми, наприклад attachBaseContext(). З цієї ж причини ви також можете вказати виробництво телефону, щоб вирішити проблему, це вирішувати саме вам.


Не працює для нас, я не можу зрозуміти, чому. Код завершується без винятку, але ми все одно отримуємо ці проблеми у звітах Crashlytics та консолі Google Play.
Антон Бреусов

5

Час очікування трансляції приймачів через 10 секунд. Можливо, ви робите асинхронний дзвінок (неправильно) з широкомовного приймача, а 4.3 фактично його виявляє.


3
Здається, марно його виявляти і недостатньо розповідати про це. Дайте нам знати, яка трансляція була б приємною.
Aaron T Harris

Пробачте, якщо я помиляюся, але я не думаю, що час очікування трансляції трансляції спричиняє саме цей збій. Доброю практикою є уникнення межі 10-х, але це інше питання, ніж у запитувача.
parkerfath

У мене просто 10 секунд на мозку. developer.android.com/training/articles/perf-anr.html IDK, якщо він також викликав збій.
danny117

Ваша думка тверда і хороша практика. Однак в оригінальному плакаті є специфічне запитання щодо конкретного набору пристроїв. Я б порадив іншим глядачам цієї публікації перевірити відповідь Крістофера та відповідь Оба, чи не спостерігаються вони з тими ж симптомами (пристрої Samsung (esp. Galaxy S 4) тощо)
parkerfath

Я тут не для того, щоб бити виробники пристроїв, це було б протилежним умовам.
danny117

5

Ось ефективне рішення від didi для вирішення цієї проблеми, оскільки ця помилка дуже поширена і важко знайти причину, вона більше схожа на системну проблему, чому ми не можемо її ігнорувати безпосередньо? Звичайно, ми можемо ігнорувати її, тут - зразок коду:

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);
        }
    }
});

Встановивши спеціальний обробник винятків за умовчанням, програма може змінити спосіб обробки оброблених винятків для тих потоків, які вже приймуть будь-яку поведінку за замовчуванням, яку надала система. Коли uncaught TimeoutExceptionвикидається з потоку з іменем FinalizerWatchdogDaemon, цей спеціальний обробник заблокує ланцюжок обробника, системний обробник не буде викликаний, тому збоїв буде уникнути.

Через практику інших поганих наслідків не виявлено. Система GC все ще працює, час зменшення часу зменшення використання процесора зменшується.

Детальніше дивіться: https://mp.weixin.qq.com/s/uFcFYO2GtWWiblotem2bGg


4

Незмінно правдою є те, що в цей час пристрій задушиться для деякої пам’яті (що зазвичай є причиною того, що GC може спрацьовувати).

Як вже згадувалося майже всіма авторами раніше, ця проблема виникає, коли Android намагається запустити GC, поки програма знаходиться у фоновому режимі. У більшості випадків, коли ми це спостерігали, користувач призупинив додаток, заблокувавши екран. Це також може вказувати на витік пам’яті десь у програмі або пристрій занадто завантажений. Тож єдиний законний спосіб мінімізувати це:

  • щоб не було витоків пам'яті, і
  • щоб зменшити слід пам'яті програми в цілому.

1
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();
}

Це не вирішить проблему, якщо тривалість сну становить> 100 секунд. Чому б не встановити його на MAX_INT?
оба

Так, я просто роблю приклад ~
kot32

1
Це не повинно працювати через постійне упорядкування. Зміна значення поля не вплине на значення, накреслене для абонентів.
hqzxzwb

0

Черга фіналізації може бути занадто довгою

я думаю, що Java може зажадати GC.SuppressFinalize () та GC.ReRegisterForFinalize (), щоб дозволити користувачу явно зменшити довжину остаточної черги

якщо вихідний код JVM доступний, може реалізувати цей метод самостійно, наприклад, виробник андроїдних ROM


0

Це здається помилкою Android Runtime. Здається, є фіналізатор, який запускається в його окремий потік і викликає метод finalize () на об'єктах, якщо вони не знаходяться в поточному кадрі стек-траса. Наприклад, наступний код (створений для перевірки цієї проблеми) закінчився збоєм.

Давайте будемо мати курсор, який щось робить у методі завершення (наприклад, SqlCipher, do close (), який блокується до бази даних, яка зараз використовується)

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)

Варіант виробництва з SqlCipher дуже подібний:

12-21 15:40:31.668: E/EH(32131): android.content.ContentResolver$CursorWrapperInner.finalize() timed out after 10 seconds
12-21 15:40:31.668: E/EH(32131): java.util.concurrent.TimeoutException: android.content.ContentResolver$CursorWrapperInner.finalize() timed out after 10 seconds
12-21 15:40:31.668: E/EH(32131): 	at java.lang.Object.wait(Native Method)
12-21 15:40:31.668: E/EH(32131): 	at java.lang.Thread.parkFor$(Thread.java:2128)
12-21 15:40:31.668: E/EH(32131): 	at sun.misc.Unsafe.park(Unsafe.java:325)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:161)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:840)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:873)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1197)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.ReentrantLock$FairSync.lock(ReentrantLock.java:200)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262)
12-21 15:40:31.668: E/EH(32131): 	at net.sqlcipher.database.SQLiteDatabase.lock(SourceFile:518)
12-21 15:40:31.668: E/EH(32131): 	at net.sqlcipher.database.SQLiteProgram.close(SourceFile:294)
12-21 15:40:31.668: E/EH(32131): 	at net.sqlcipher.database.SQLiteQuery.close(SourceFile:136)
12-21 15:40:31.668: E/EH(32131): 	at net.sqlcipher.database.SQLiteCursor.close(SourceFile:510)
12-21 15:40:31.668: E/EH(32131): 	at android.database.CursorWrapper.close(CursorWrapper.java:50)
12-21 15:40:31.668: E/EH(32131): 	at android.database.CursorWrapper.close(CursorWrapper.java:50)
12-21 15:40:31.668: E/EH(32131): 	at android.content.ContentResolver$CursorWrapperInner.close(ContentResolver.java:2746)
12-21 15:40:31.668: E/EH(32131): 	at android.content.ContentResolver$CursorWrapperInner.finalize(ContentResolver.java:2757)
12-21 15:40:31.668: E/EH(32131): 	at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:222)
12-21 15:40:31.668: E/EH(32131): 	at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:209)
12-21 15:40:31.668: E/EH(32131): 	at java.lang.Thread.run(Thread.java:762)

Резюме: Закрийте курсори якнайшвидше. Принаймні, на Samsung S8 з Android 7, де видно проблему.


0

Для створених вами класів (тобто не є частиною Android) можна повністю уникнути аварії.

Будь-який клас, який реалізує, finalize()має певну неминучу ймовірність збоїв, як пояснив @oba. Тому замість використання фіналізаторів для очищення використовуйте a PhantomReferenceQueue.

Для прикладу ознайомтеся з реалізацією в React Native: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/jni/DestructorThread.java

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.