List.subListメソッドで発生するStackOverflowError
インラインプッシュ
<ブロッククオート[長期有効】Byte Jump Meチームへの参加を歓迎します。 インサイダーリンク
0x1.例外の概要
java.lang.StackOverflowError
java.util.AbstractList$SubAbstractList.listIterator(AbstractList.java:308)
java.util.AbstractList$SubAbstractList.listIterator(AbstractList.java:308)
......
java.util.AbstractList$SubAbstractList.listIterator(AbstractList.java:308)
java.util.AbstractList$SubAbstractList.listIterator(AbstractList.java:308)
java.util.AbstractList$SubAbstractList.iterator(AbstractList.java:301)
java.util.AbstractCollection.toArrayList(AbstractCollection.java:349)
java.util.AbstractCollection.toArray(AbstractCollection.java:339)
java.util.Collections.sort(Collections.java:1863)
このクラッシュは、Android 4.4 以下 (<=JDK 6) のみで発生し、5.0 (>=JDK 7) 以上では発生しない。
0x2. 解析と解決
1) システムバージョン固有のバグなので、androidxrefで対応するシステムバージョンのソースコードを確認する。(アンドロイド4.4)
2) 再度クラッシュスタックを見ると、Collections.sort(Collections.java:1863)メソッドはList.toArrayメソッドを呼び出して再度ソートします。collections.sortは次のように表示されています。
質問です。直感的にコードを見ると、mNewCommingUsersTempはArrayListであり、ArrayListは自身をtoArrayに実装し、AbstractCollection.toArray(AbstractCollection.java:339)に行くべきではなく、StackOverflowは、AbstractList$SubAbstractList.listIterator(AbstractList.java:308)の再帰的呼び出しが原因になって起こっているのだと思います。
回答 だから、mNewCommingUsersTempの参照はArrayListオブジェクトからListインターフェースを実装する別のオブジェクトに変更する必要があります。 mNewCommingUsersTempはこの文で再割り当てされます: mNewCommingUsersTemp = mNewCommingUsersTemp.subList(0, NEW_COMMING_LIST_MAX_SIZE) だからそれは基本的にサブリストの鍋にロックされています。
3) subListメソッドについて。
mNewCommingUsersTempはArrayListオブジェクトとしてスタートしたので、ソースコードからsubListメソッドを探してみたところ、ArrayListにはsubListメソッドがなく、その親クラスAbstractListに実装があることが判明しました。
つまり、subListメソッドが新しいSubAbstractListオブジェクトを生成するたびに、親リストを参照し、サブの位置を記録するだけの実装になっているのです。
つまり、複数subListの処理は、SubAbstractListオブジェクトの数だけsubすることです。
ArrayList --> SubAbstractList A --> SubAbstractList B --> SubAbstractList C --> ...
4) Collections.sortがクラッシュする理由
再びエラースタックに戻ると、AbstractList$SubAbstractList.listIterator(AbstractList.java:308)は以下のように再帰的な呼び出しを生成して実装しています。
fullListはSubAbstractListの親なので、SubAbstractList Cを反復するときは、当然SubAbstractList C --> SubAbstractList B --> SubAbstractList A --> ArrayListから反復することになります。
5) 解決方法
mNewCommingUsersTemp = mNewCommingUsersTemp.subList(0, NEW_COMMING_LIST_MAX_SIZE) とします。
に変更する。
mNewCommingUsersTemp = new ArrayList<>(mNewCommingUsersTemp.subList(0, NEW_COMMING_LIST_MAX_SIZE)) となります。
SubListをArrayListに変換し、複数のSubが再帰につながる問題を回避しています。
0x3. 考えること、広がること
1) Android 5.0+ (>=JDK 7) はなぜクラッシュしないのですか?
JDK 7+のArrayListはAbstractListのsubListメソッドを再インプリメントしており、依然として新しいSubListオブジェクトを生成しています。
しかし、ArrayList は独自の SubList のセットを実装しており、AbstractList を使用することはもうありません。
S
u
b
A
b
s
t
r
a
c
t
L
i
s
t
.
そして
A
r
r
a
y
L
i
s
t
SubAbstractListです。とArrayList
<スパン
S
u
b
A
b
s
t
r
a
c
t
L
i
s
t
.
そして
A
r
r
a
y
L
i
s
t
サブリスト
L
i
s
t
I
t
e
r
a
t
o
r
ほぼ
ほとんど
レ
新規
実現
実現した
l
l
i
s
t
I
t
e
r
a
t
o
r
メソッド
方法
異なる
とは異なる
で
A
b
s
t
r
a
c
t
L
i
s
t
ListIteratorはほぼ再実装、AbstractListとlistIteratorメソッドは別物
<スパン
L
i
s
t
I
t
e
r
a
t
o
r
一二
ほぼ
再
新しい
リアル
プレゼント
その
l
i
s
t
I
t
e
r
a
t
o
r
スクエア
メソッド
いいえ
と同じ
で
<スパン
A
b
s
t
r
a
c
t
L
i
s
t
SubAbstractList$SubAbstractListIterator は、再帰的でなくなるため、Stack Overflow に戻ることはありません。
2) JDK 7 の ArrayList$SubList.remove メソッドは以下の通りである。
JDK 6 AbstractList$SubAbstractList.remove メソッドは、以下のとおりです。
どちらの方法も基本的に同じものを実装しており、どちらも上方再帰のためにparentを呼び出しますが、これはStack Overflowですか?
回答 はい、そうです。
検証用のデモ(ループ)を書いてください。
One Plus 5T Android 8:スタックが8MになるとStack Overflowになり、この時点で深さは約9万回になります。1秒間に2回subListすると、この深さに到達するのに10時間以上かかる。
ソニーZ1 Android 4.4。深さが500回程度の時にスタックオーバーフローが発生する。
そのため、JDKの高いバージョンでは、メソッドスタッキングのメカニズムが再設計されている可能性があります。
JDKの高いバージョンはクラッシュしにくいのですが、深くなればなるほどSubListオブジェクトの追加や削除に時間がかかり、アプリがラグる原因になるという落とし穴があるのです
関連
-
AndroidStudioのコンパイル時のエラーを解決します。構成 ':classpath' のすべてのアーティファクトを解決できませんでした。
-
AndroidStudioは、新しいプロジェクト:エミュレータを作成した後、エラーを報告します。ERROR: x86 emulation currently requires hardware acceleration!
-
Error:タスク ':app:compileDebugJavaWithJavac' の実行に失敗しました。解決方法
-
ArrayAdapter のソリューションでは、リソース ID が TextView である必要があります。
-
Android 開発の問題 - いくつかのプロジェクトはすでにワークスペースに存在するため、インポートできません。
-
BUG: android アクティビティはエクスポートされるか、インテント・フィルタを含む必要があります。
-
Android eclipseが起動できない:選択項目を起動できず、最近起動した項目もない
-
My Android Notes (XI) - 環境設定を使って設定を保存する
-
MPAndroidChartのPieChartで、セクターが表示されず、中央のテキストのみが表示される。
-
Android開発日記】SwipeRefreshLayoutにプルアップ読み込み機能を追加しました
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[100%効果的】AndroidStudioコンソールコンパイル出力中国の乱雑なコードを解決する、プロテストソリューション!
-
GPSプロンプトの問題は、Callがユーザーによって拒否される可能性のある許可を必要とすることです:コードは、明示的にsをチェックする必要があります。
-
Android Handlerのメッセージングメカニズムの最も完全な説明(継続的に追加される)。
-
jniとjavaの間でbytearrayを受け渡しする
-
ログアウトしたエラー: java.lang.RuntimeException: アクティビティを開始できません ComponentInfo
-
SolutionMethod がスーパークラスのメソッドをオーバーライドしない。
-
Android ConstraintLayout コンストレイントレイアウト
-
Android TextViewの自動改行問題
-
android.view.inflateexception バイナリ xml ファイル行例外の解決方法
-
java.util.Iterator java.util.List.iterator()' で null オブジェクト参照例外が発生した場合の解決策を紹介します。