Repository navigation
Let a state drop a destroyed controller; pool tile scratch buffers - #26
Merged
Merged
Conversation
…stroy The state outlives the map: apps keep it (the example app keeps one per provider), and it held the last controller after the map was torn down, and through it every overlay entity of that map. detachController(controller) lets go of it, unless another controller has been attached since. Overlay controllers' destroy() now clears their managers, so a destroyed controller that is still referenced keeps no entities. An add() in flight when that happens stops instead of tripping over the missing entity.
TilePngEncoder and MarkerTileRenderer kept their scratch (a ~1.2 MB direct buffer at this tablet's tile size, an output stream) in a ThreadLocal. Tiles are drawn on the map SDK's threads, and Google Maps starts a pool of three "androidmapsapi-tileoverlay" threads for every map and never shuts it down, so each destroyed Google map left three live threads with a buffer each: about 1.7 MB of Java heap per provider switch that nothing could free (heap dumps after GC, MapLibre <-> Google with Boston parcels). ScratchPool keeps at most a few of them (about one per core) whatever the threads do. The threads themselves are the SDK's and remain.
This was referenced Nov 8, 2026
wf9a5m75
added a commit
that referenced
this pull request
Nov 8, 2026
Provider CI builds this branch of core with compose's default branch, which already uses MapViewState.detachController(controller) from develop (#26).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
なぜ
プロバイダを切り替えるたびに前の地図がメモリに残り、区画データ(約 10 万ポリゴン)を出したまま数回切り替えると OutOfMemoryError で落ちていました。原因はいくつかのリポジトリにまたがっていて、core が持っているのは次の 2 つです。
TilePngEncoderとMarkerTileRendererは、作業用のバッファ(このタブレットのタイルサイズで約 1.2 MB)をThreadLocalに入れていました。タイルは地図 SDK のスレッドで描かれます。Google Maps は地図を作るたびにandroidmapsapi-tileoverlayのスレッドを 3 本作り、地図を破棄しても止めません。そのため Google の地図を 1 回作るごとに、生きたスレッド 3 本とバッファ 3 つが残り、切り替え 1 回あたり約 1.7 MB ずつ増えていました。何を変えたか
MapViewState.detachController(controller)を追加しました(公開 API の追加)。渡された controller がまだ接続中のときだけ外します。呼ぶのは compose のMapViewBaseです。destroy()で、manager をclear()するようにしました。破棄済みのコントローラがどこかから参照されていても、エンティティは残りません。破棄と並走したadd()は、!!で落ちずに早期 return します。ScratchPool(internal)を追加し、TilePngEncoderとMarkerTileRendererのThreadLocalの作業用バッファを置き換えました。スレッドがいくつ増えても、プールが持つのは最大でコア数程度(2〜8 個)です。スレッド自体は SDK のもので、こちらからは止められません。.apiに載っています。テスト:
detachControllerの 2 本とScratchPoolの 3 本を足しました。関連 PR(同じ修正の一式)
fix/mapview-teardownは次のリポジトリにまたがります。core → compose → 各プロバイダの順にマージしてください。プロバイダの変更は、core のMapViewState.detachControllerと、compose のMapViewBase(onDestroyMapView = …)/MapViewLifecycleOwnerを使います。検証
タブレット(HA26Z6JL)の example-app で確認しました。区画データ(約 10 万ポリゴン)はポリゴンのタイル描画が要るので、検証には、このブランチのコミットを
feat/polygon-tiling/feat/overlay-on-addedの上に一時的に cherry-pick したブランチ(test/teardown-on-tiling、push していません)を使っています。am dumpheap -gで GC させて、GC 後のヒープを測りました。MapLibreViewController/GoogleMapViewControllerが残り、それぞれ 98,843 件の PolygonEntity を抱えていました。testDebugUnitTestとapiCheckを流しました。新しく足したテストはすべて通っています。失敗は次の 5 つで、どれも develop の時点で同じ理由で落ちています。確認できていないもの: