Мы видим число 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)
До сих пор нам не удавалось воспроизвести проблему дома или выяснить, что могло ее вызвать.
Есть идеи, что может вызвать это? Любая идея, как отладить это и выяснить, какая часть приложения вызывает это? Все, что проливает свет на проблему, помогает.
Больше 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
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
Ответы:
Полное раскрытие - я автор ранее упомянутой беседы в TLV DroidCon.
У меня была возможность изучить эту проблему во многих приложениях для Android и обсудить ее с другими разработчиками, которые сталкивались с ней - и мы все пришли к одному и тому же вопросу: эту проблему нельзя избежать, только свести к минимуму.
Я более внимательно посмотрел на реализацию кода сборщика мусора Android по умолчанию, чтобы лучше понять, почему выбрасывается это исключение и каковы возможные причины этого. Я даже нашел возможную причину во время экспериментов.
Корень проблемы в том, что устройство некоторое время «спит» - это означает, что ОС решила снизить потребление батареи, на время остановив большинство процессов User Land, и выключив Screen, уменьшив циклы ЦП. и т. д. То, как это делается, - на уровне системы Linux, где процессы приостановлены в середине выполнения. Это может произойти в любое время во время нормального выполнения приложения, но оно остановится на системном вызове Native, поскольку переключение контекста выполняется на уровне ядра. Итак - вот где Dalvik GC присоединяется к истории.
Код Dalvik GC (реализованный в проекте Dalvik на сайте AOSP) не является сложным фрагментом кода. Как это работает, описано в моих слайдах по DroidCon. То, что я не рассмотрел, - это основной цикл GC - в тот момент, когда у сборщика есть список объектов для завершения (и уничтожения). Логика цикла у основания может быть упрощена следующим образом:
starting_timestamp
,finalize()
иdestroy()
при необходимости вызвать nativeend_timestamp
,end_timestamp - starting_timestamp
) и сравнить с жестко закодированным значением времени ожидания 10 секунд,java.util.concurrent.TimeoutException
и убить процесс.Теперь рассмотрим следующий сценарий:
Приложение работает, делая свое дело.
Это не пользовательское приложение, оно работает в фоновом режиме.
Во время этой фоновой операции объекты создаются, используются и должны быть собраны для освобождения памяти.
Приложение не работает с WakeLock - это отрицательно скажется на аккумуляторе и кажется ненужным.
Это означает, что приложение будет время от времени вызывать GC.
Обычно GC проходит без заминки.
Иногда (очень редко) система решает спать в середине запуска GC.
Это произойдет, если вы запустите приложение достаточно долго и внимательно наблюдаете за журналами памяти Dalvik.
Теперь - рассмотрим логику временной метки основного цикла GC - устройство может начать выполнение, выполнить
start_stamp
и перейти в спящий режим приdestroy()
собственном вызове системного объекта.Когда он проснется и возобновит выполнение,
destroy()
завершится, и следующимend_stamp
будет время, которое потребовалось дляdestroy()
вызова + время ожидания.Если время сна было долгим (более 10 секунд),
java.util.concurrent.TimeoutException
будет выброшено.Я видел это на графиках, сгенерированных из скрипта Python для анализа - для системных приложений Android, а не только для моих собственных контролируемых приложений.
Соберите достаточно логов, и вы в конечном итоге увидите это.
Нижняя граница:
Эту проблему невозможно избежать - вы столкнетесь с ней, если ваше приложение будет работать в фоновом режиме.
Вы можете смягчить воздействие, взяв WakeLock, и не дать устройству спать, но это совсем другая история, и новая головная боль, и, возможно, еще один разговор в другом мошенничестве.
Вы можете минимизировать проблему, сократив количество вызовов GC - делая сценарий менее вероятным (советы приведены на слайдах).
У меня еще не было возможности просмотреть код GC Dalvik 2 (также известный как ART), который может похвастаться новой функцией сжатия поколений, или провести какие-либо эксперименты на Android Lollipop.
Добавлено 05.07.2015:
После рассмотрения агрегации отчетов о сбоях для этого типа сбоя, похоже, что эти вылеты из версии 5.0+ ОС Android (Lollipop с ART) составляют только 0,5% от этого типа сбоя. Это означает, что изменения ART GC уменьшили частоту этих сбоев.
Добавлено 01.06.2016:
Похоже, проект Android добавил много информации о том, как работает GC в Dalvik 2.0 (он же ART).
Вы можете прочитать об этом здесь - Отладка ART Сборка мусора .
Также обсуждаются некоторые инструменты для получения информации о поведении GC для вашего приложения.
Отправка SIGQUIT процессу вашего приложения по существу вызовет ANR и сохранит состояние приложения в файл журнала для анализа.
источник
Мы видим это постоянно, по всему нашему приложению, используя Crashlytics. Сбой обычно происходит в коде платформы. Небольшая выборка:
Устройства, на которых это происходит, в подавляющем большинстве (но не исключительно) являются устройствами, произведенными Samsung. Это может означать, что большинство наших пользователей используют устройства Samsung; альтернативно это может указывать на проблему с устройствами Samsung. Я не совсем уверен.
Я полагаю, что это на самом деле не отвечает на ваши вопросы, но я просто хотел подчеркнуть, что это кажется довольно распространенным явлением и не относится к вашему приложению.
источник
Я нашел несколько слайдов по этой проблеме.
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
источник
Мы решили проблему, остановив
FinalizerWatchdogDaemon
.Вы можете вызвать метод в жизненном цикле приложения, например
attachBaseContext()
. По той же причине вы также можете указать производителя телефона, чтобы решить проблему, решать вам.источник
Тайм-аут приемников вещания через 10 секунд. Возможно, вы делаете асинхронный (неправильный) вызов от широковещательного приемника, а 4.3 фактически обнаруживает его.
источник
Вот эффективное решение от Didi для решения этой проблемы, так как эта ошибка очень распространена и ее трудно найти, она больше похожа на системную проблему. Почему мы не можем игнорировать ее напрямую? Конечно, мы можем игнорировать ее, Здесь это пример кода:
Установив специальный обработчик неперехваченных исключений по умолчанию, приложение может изменить способ обработки неперехваченных исключений для тех потоков, которые уже приняли бы любое поведение по умолчанию, предоставленное системой. Когда
TimeoutException
из именованного потока выбрасывается uncaughtFinalizerWatchdogDaemon
, этот специальный обработчик блокирует цепочку обработчиков, системный обработчик вызываться не будет, поэтому сбоя избежать не будет.На практике никаких других негативных последствий обнаружено не было. Система GC все еще работает, таймауты уменьшаются по мере уменьшения загрузки процессора.
Для получения дополнительной информации см .: https://mp.weixin.qq.com/s/uFcFYO2GtWWiblotem2bGg
источник
Одна вещь, которая неизменно верна, состоит в том, что в это время устройство задыхается от некоторой памяти (что обычно является причиной того, что GC, скорее всего, сработает).
Как уже упоминалось почти всеми авторами ранее, эта проблема возникает, когда Android пытается запустить GC, когда приложение находится в фоновом режиме. В большинстве случаев, когда мы это наблюдали, пользователь приостанавливал приложение, блокируя свой экран. Это также может указывать на утечку памяти где-либо в приложении или на устройство, уже загруженное. Таким образом, единственный законный способ минимизировать это:
источник
источник
FinalizeQueue может быть слишком длинным
я думаю, что Java может потребовать GC.SuppressFinalize () и GC.ReRegisterForFinalize (), чтобы позволить пользователю явно уменьшить длину finalizedQueue
если исходный код JVM доступен, мы можем реализовать этот метод самостоятельно, например, Android ROM Maker
источник
Это похоже на ошибку Android Runtime. Кажется, что есть финализатор, который работает в отдельном потоке и вызывает метод finalize () для объектов, если они не находятся в текущем кадре трассировки стека. Например, следующий код (созданный для проверки этой проблемы) завершился сбоем.
Давайте возьмем некоторый курсор, который делает что-то в методе finalize (например, SqlCipher, do close (), который привязывается к базе данных, которая используется в данный момент)
И мы делаем несколько длительных вещей, открыв курсор:
Это вызывает следующую ошибку:
Вариант производства с SqlCipher очень похож:
Резюме: Закрыть курсоры как можно скорее. По крайней мере, на Samsung S8 с Android 7, где проблема была замечена.
источник
Для классов, которые вы создаете (то есть не являетесь частью Android), можно полностью избежать сбоя.
Любой реализующий класс
finalize()
имеет некоторую неизбежную вероятность сбоя, как объясняет @oba. Таким образом, вместо использования финализаторов для выполнения очистки, используйтеPhantomReferenceQueue
.Для примера посмотрите реализацию в React Native: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/jni/DestructorThread.java
источник