처리 방법 : java.util.concurrent.TimeoutException : android.os.BinderProxy.finalize ()가 10 초 오류 후에 시간 초과 되었습니까? 추적 : 1

에 및 의 숫자가 표시 TimeoutExceptions됩니다 . 이 중 90 % 이상이 Android 4.3에서 발생합니다. 우리는 현장의 사용자들로부터 크 리터 시즘으로부터 이것을보고하고 있습니다.GcWatcher.finalize, BinderProxy.finalizePlainSocketImpl.finalize

여기에 이미지 설명을 입력하십시오

오류는 ” 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

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 가비지 콜렉터 코드의 기본 구현을 자세히 살펴 보았습니다. 실험하는 동안 가능한 근본 원인을 발견했습니다.

문제의 근본 원인은 장치가 잠시 “휴면 상태로 전환”된 시점에 있습니다. 즉, OS는 대부분의 사용자 랜드 프로세스를 잠시 중지하고 화면을 끄고 CPU주기를 줄임으로써 배터리 소비를 줄이기로 결정했음을 의미합니다. 등이 수행되는 방식은 프로세스가 실행 중 일시 중지되는 Linux 시스템 수준입니다. 이는 정상적인 응용 프로그램 실행 중에 언제든지 발생할 수 있지만 컨텍스트 전환이 커널 수준에서 수행되므로 기본 시스템 호출에서 중지됩니다. Dalvik GC가 이야기에 참여하는 곳입니다.

Dalvik GC 코드 (AOSP 사이트의 Dalvik 프로젝트에서 구현 된)는 복잡한 코드가 아닙니다. 기본 작동 방식은 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 루프의 타임 스탬프 로직을 고려하십시오. 장치가 실행을 시작 하고을 수행하고 시스템 객체 start_stampdestroy()기본 호출에서 절전 모드로 전환 할 수 있습니다.

깨어나서 달리기를 재개하면 destroy()의지가 끝나고 다음 end_stampdestroy()전화에 걸린 시간 + 수면 시간이됩니다.

수면 시간이 길면 (10 초 이상) java.util.concurrent.TimeoutException던질 것입니다.

나는 모니터링 파이썬 스크립트에서 생성 된 그래프에서 이것을 보았습니다. 내 자신의 모니터링 된 앱뿐만 아니라 Android 시스템 응용 프로그램 용.

충분한 로그를 수집하면 결국 볼 수 있습니다.

결론 :

문제를 피할 수 없습니다. 앱이 백그라운드에서 실행되면 문제가 발생합니다.

WakeLock을 사용하여 완화하고 장치가 절전 모드로 전환되는 것을 방지 할 수는 있지만 완전히 다른 이야기와 새로운 두통, 또 다른 단점이있을 수 있습니다.

GC 통화를 줄이면 문제를 최소화 할 수 있습니다. 시나리오의 가능성이 줄어 듭니다 (슬라이드에 팁이 있음).

아직 새로운 세대 압축 기능을 자랑하거나 Android Lollipop에서 실험을 수행 한 Dalvik 2 (일명 ART) GC 코드를 살펴볼 기회가 없었습니다.

2015 년 7 월 5 일 추가됨 :

이 충돌 유형에 대한 충돌 보고서 집계를 검토 한 후 Android OS 버전 5.0 이상의 ART (Lollipop with ART)의 충돌은이 충돌 유형의 0.5 % 만 차지하는 것으로 보입니다. 이는 ART GC 변경으로 인해 이러한 충돌 빈도가 감소했음을 의미합니다.

2016 년 6 월 1 일 추가됨 :

Android 프로젝트에서 GC가 Dalvik 2.0 (일명 ART)에서 작동하는 방식에 대한 많은 정보를 추가 한 것 같습니다.

ART 가비지 콜렉션 디버깅 -여기에서 읽을 수 있습니다 .

또한 앱의 GC 동작에 대한 정보를 얻는 도구에 대해서도 설명합니다.

SIGQUIT를 앱 프로세스로 보내면 본질적으로 ANR이 발생하고 분석을 위해 애플리케이션 상태를 로그 파일로 덤프합니다.


답변

우리는 Crashlytics를 사용하여 앱 전체에서 지속적으로 이것을보고 있습니다. 충돌은 일반적으로 플랫폼 코드에서 발생합니다. 작은 샘플링 :

10 초 후 android.database.CursorWindow.finalize () 시간이 초과되었습니다

10 초 후에 java.util.regex.Matcher.finalize () 시간이 초과되었습니다.

android.graphics.Bitmap $ BitmapFinalizer.finalize ()가 10 초 후에 시간 초과되었습니다

10 초 후에 org.apache.http.impl.conn.SingleClientConnManager.finalize ()가 시간 초과되었습니다

10 초 후에 java.util.concurrent.ThreadPoolExecutor.finalize () 시간이 초과되었습니다.

10 초 후 android.os.BinderProxy.finalize () 시간이 초과되었습니다

10 초 후에 android.graphics.Path.finalize () 시간이 초과되었습니다

이 문제가 발생하는 장치는 삼성이 제조 한 압도적으로 (단독은 아님) 장치입니다. 이는 대부분의 사용자가 삼성 기기를 사용하고 있음을 의미 할 수 있습니다. 또는 삼성 기기에 문제가있을 수 있습니다. 확실하지 않습니다.

이것이 실제로 귀하의 질문에 대답하지는 않지만 이것이 매우 일반적이며 응용 프로그램에 국한되지 않는다는 것을 강조하고 싶었습니다.


답변

이 문제에 대한 슬라이드를 발견했습니다.

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.

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(). 같은 이유로, 당신은 또한 문제를 해결하기 위해 전화의 제조를 지정할 수 있습니다, 그것은 당신에게 달려 있습니다.


답변

10 초 후 브로드 캐스트 리시버 제한 시간. 브로드 캐스트 수신기에서 비동기 호출 (잘못된)을 수행하면 실제로 4.3이 감지 할 수 있습니다.


답변

이 문제를 해결하기위한 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);
        }
    }
});

응용 프로그램은 특수한 기본 포착되지 않은 예외 처리기를 설정하여 시스템이 제공 한 기본 동작을 이미 수락 한 스레드에 대해 포착되지 않은 예외가 처리되는 방식을 변경할 수 있습니다. 잡히지 않은 TimeoutException스레드에서 잡히지 않을 때FinalizerWatchdogDaemon 않으면이 특수 핸들러는 핸들러 체인을 차단하고 시스템 핸들러는 호출되지 않으므로 충돌을 피할 수 있습니다.

연습을 통해 다른 나쁜 영향은 발견되지 않았습니다. GC 시스템은 여전히 ​​작동하고 있으며 CPU 사용량이 감소함에 따라 시간 초과가 완화됩니다.

자세한 내용은 https://mp.weixin.qq.com/s/uFcFYO2GtWWiblotem2bGg를 참조하십시오.


답변

항상 사실 중 하나는이 시점에서 장치가 일부 메모리에 대해 질식 할 것입니다 (일반적으로 GC가 트리거되는 이유).

이전에 거의 모든 저자가 언급했듯이이 문제는 앱이 백그라운드에있는 동안 Android에서 GC를 실행하려고 할 때 발생합니다. 우리가 관찰 한 대부분의 경우, 사용자는 화면을 잠 가서 앱을 일시 중지했습니다. 또한 응용 프로그램 어딘가에서 메모리 누수가 발생했거나 장치가 이미로드되어 있음을 나타냅니다. 따라서 그것을 최소화하는 유일한 합법적 인 방법은 다음과 같습니다.

  • 메모리 누수가 없는지 확인하고
  • 일반적으로 앱의 메모리 사용량을 줄입니다.