: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

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

エラーがスローされるコード行は、2013年6月5日にリリースされたバージョン4.3_r1で導入されました。それ以降、問題が発生している可能性があります。
edubriguenti 2014年

Androidバージョン4.2.2もこの例外をスローし始めたので、おそらくそれがソースであるGoogle Playのアップデートです。
JWqvist

@EvelioTarazona play-servicesを使用しない一部のアプリで使用しています
ligi

@ligiはあなたにとって同じスタックトレースですか?
eveliotc 2015

回答:


220

完全な開示 -私は、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_stampスリープ状態になる可能性がありdestroy()ます。

ウェイクアップして実行を再開すると、destroy()は終了し、次end_stampdestroy()コールにかかった時間+スリープ時間になります。

スリープ時間が長い(10秒を超える)場合、java.util.concurrent.TimeoutExceptionがスローされます。

私はこれを分析pythonスクリプトから生成されたグラフで見ました-私自身の監視されているアプリだけでなく、Androidシステムアプリケーション用です。

十分なログを収集すると、最終的にそれが表示されます。

結論:

この問題は回避できません。アプリがバックグラウンドで実行されている場合に発生します。

WakeLockを使用して緩和し、デバイスがスリープ状態になるのを防ぐことができますが、それはまったく別の話であり、新しい頭痛の種であり、おそらく別の詐欺での別の話です。

GC呼び出しを減らすことで問題を最小限に抑えることができます-シナリオの可能性を低くします(スライドにヒントがあります)。

私はまだ、Dalvik 2(別名ART)GCコードを調べる機会がありませんでした。これは、新しい世代別コンパクト化機能を誇り、Android Lollipopで実験を行いました。

2015年7月5日追加:

このクラッシュタイプのクラッシュレポートの集計を確認したところ、Android OS(ART付きLollipop)のバージョン5.0以降からのこれらのクラッシュは、このクラッシュタイプの0.5%しか占めていないようです。これは、ART GCの変更により、これらのクラッシュの頻度が減少したことを意味します。

2016年6月1日追加:

Androidプロジェクトにより、Dalvik 2.0(別名ART)でのGCの動作に関する多くの情報が追加されたようです。

これについては、ARTガベージコレクションのデバッグを参照してください。

また、アプリのGC動作に関する情報を取得するためのいくつかのツールについても説明します。

SIGQUITをアプリプロセスに送信すると、基本的にANRが発生し、分析のためにアプリケーションの状態がログファイルにダンプされます。


私の場合、バックグラウンドで実行しているコード/時間の量を減らす方法を見つけることでこれを軽減することも計画しています。このトピックについてお調べいただきありがとうございます。
parkerfath 2015年

アプリで行われたバックグラウンド処理を削除すると、問題の軽減に大きく役立ちます。
oba 2015

価値があることについては、これはまだマシュマロ(6.0.1)で起こります。そうは言っても、私はこのエラーを一度しか受けたことがない。したがって、それは大きな問題ではないようです。十分な説明ありがとうございます。
クノッソス2016年

しばらくして、OSでこの問題を修正することは非常に問題があり、GoogleとOEM間の協力が必要であるという明確な印象を受けました。これがすぐに修正されるとは思いません。
oba 2016年

wakelockを使用していますが、Android 4.4.2でもこの問題が発生します。私のアプリにはいくつかのバックグラウンド操作がありますが、主に充電ケーブルを取り付けたまま一日中動作するように設計されています。この問題を軽減する別の方法はありますか?
Orcun Sevsay

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 2015

4
XIAOMIで製造されたデバイスを搭載したAndroid 4.4.4でこの問題が発生します
Paresh Dudhat

サムスンのタブレットでこれらのクラッシュの大部分が発生しているということを知らせたかっただけです。samsungがタブレットがバックグラウンドアプリをどのように処理するかで、どのように異なった動作をしたかはわかりません。
FriendlyMikhail

1
私はアンドロイド4.4.4でこの問題を抱えています。HUAWEIによって製造されたデバイス。
Rameshbabu 2016年

1
Android 5.0.2 Samsungデバイスでリークカナリアライブラリを使用すると、アプリがクラッシュします。ライブラリの初期化を無効にすると、アプリは正常に動作します。
vanomart 2016年

15

この問題に関するスライドをいくつか見つけました。

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


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 Consoleでこれらの問題が発生します。
アントンBreusov

5

ブロードキャストレシーバーは10秒後にタイムアウトします。おそらく、ブロードキャストレシーバーからの非同期呼び出し(間違った)を実行すると、4.3が実際にそれを検出します。


3
それを検出することは役に立たないようであり、それについて十分に伝えていません。どの放送がいいか教えてください。
アーロンTハリス

私が間違っている場合はご容赦ください。ただし、ブロードキャストレシーバーのタイムアウトによってこの特定のクラッシュが発生するとは思わない 10秒の制限を回避することをお勧めしますが、これは要求者が抱えている問題とは別の問題です。
parkerfath 14

私は脳に10秒しかありません。クラッシュの原因になっている場合は、developer.android.com / training / articles / perf-anr.html IDK。
danny117 2014

あなたの主張はしっかりしていて、良い習慣です。ただし、元の投稿者には、特定のデバイスセットに関する特定の質問があります。この投稿の他の閲覧者にも、同じ症状(Samsungデバイス(特にGalaxy S 4)など)が発生している場合は、クリストファーの回答とobaの回答を確認することをお勧めします
Parkerfath

私はここにいるのは、デバイスの製造元を打ち負かすことではありません。
danny117 2015年

5

これはこの問題を解決するためのディディからの効果的な解決策です。このバグは非常に一般的で原因を見つけるのが難しいため、システムの問題のように見えます。なぜ直接無視できないのですか?もちろん、ここでは無視できます。サンプルコードです:

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を参照してください。


4

常に当てはまることの1つは、現時点でデバイスが一部のメモリを圧迫していることです(これは通常、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

finalizeQueueが長すぎる可能性があります

javaは、ユーザーがfinalizedQueueの長さを明示的に減らすために、GC.SuppressFinalize()およびGC.ReRegisterForFinalize()を必要とする可能性があると思います

JVMのソースコードが利用可能な場合、Android ROMメーカーなど、これらのメソッドを自分で実装できます。


0

Androidランタイムのバグのようです。個別のスレッドで実行され、スタックトレースの現在のフレームにないオブジェクトに対してfinalize()メソッドを呼び出すファイナライザーがあるようです。たとえば、次のコード(この問題を検証するために作成)はクラッシュで終了しました。

finalizeメソッドで何かを行うカーソルを用意しましょう(たとえば、SqlCipherのカーソル、現在使用中のデータベースにロックする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)

再開:できるだけ早くカーソルを閉じます。問題が確認されているAndroid 7搭載のSamsung S8で少なくとも。


0

作成したクラス(つまり、Androidの一部ではないクラス)の場合、クラッシュを完全に回避することができます。

finalize()@obaで説明されているように、実装するクラスはクラッシュする可能性が避けられません。そのため、ファイナライザを使用してクリーンアップを実行する代わりに、を使用しPhantomReferenceQueueます。

例として、React Nativeの実装を確認してくださいhttps : //github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/jni/DestructorThread.java

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.