Lots of OutOfMemory exceptions
Checking Play Store stats for release 2025.08.31 I can see a couple of different OutOfMemoryError Exceptions. They happen in different places, and have occurrences between 50 and 120 times for release 2025.08.31
We never had a relevant amount of OOM in the past (at least not in the last five years or so), but it has increased significantly over the last couple of versions, so something must have changed in terms of memory consumption.
Here are some pointers:
- io.reactivex.rxjava3.internal.schedulers.NewThreadWorker.scheduleActual (127 occurrences)
- r8-map-id-b7adbe2606c2bd03ddd012ac7363f6b5e39bf2b413ac232f502f493d621b1e98 - j$.time.Duration.i (81 occurrences)
- cgeo.geocaching.activity.AbstractActivity.onStart (76 occurrences)
- io.reactivex.rxjava3.internal.schedulers.NewThreadWorker.scheduleActual (56 occurrences)
- android.app.QueuedWork.processPendingWork (52 occurrences) (and probably more with fewer occurrences, have checked only the first page of crashes)
Anything we can do to nail this down, to find out which part of c:geo is consuming so much memory lately?
For the sake of completeness, here are the logs for those errors (same order as above), although are probably not indicating the actual reason:
Exception io.reactivex.rxjava3.exceptions.UndeliverableException:
at io.reactivex.rxjava3.plugins.RxJavaPlugins.onError (SourceFile:372)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run (SourceFile:83)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call (SourceFile:71)
at java.util.concurrent.FutureTask.run (FutureTask.java:317)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run (ScheduledThreadPoolExecutor.java:348)
at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1156)
at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:651)
at java.lang.Thread.run (Thread.java:1119)
Caused by java.lang.OutOfMemoryError:
at io.reactivex.rxjava3.internal.schedulers.NewThreadWorker.scheduleActual (SourceFile:131)
at io.reactivex.rxjava3.internal.schedulers.NewThreadWorker.schedule (SourceFile:50)
at io.reactivex.rxjava3.core.Scheduler$Worker$PeriodicTask.run (SourceFile:565)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run (SourceFile:80)
Exception java.lang.OutOfMemoryError:
at j$.time.Duration.i (r8-map-id-b7adbe2606c2bd03ddd012ac7363f6b5e39bf2b413ac232f502f493d621b1e98:12)
at j$.time.Duration.ofNanos (r8-map-id-b7adbe2606c2bd03ddd012ac7363f6b5e39bf2b413ac232f502f493d621b1e98:16)
at m140.bpc$a.run (:com.google.android.gms.policy_maps_core_dynamite@[email protected]:18)
at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:641)
at m140.bpr.run (:com.google.android.gms.policy_maps_core_dynamite@[email protected]:40)
at java.lang.Thread.run (Thread.java:920)
Exception java.lang.OutOfMemoryError:
at com.google.maps.api.android.lib6.impl.z.<init> (:com.google.android.gms.policy_maps_core_dynamite@[email protected]:398)
at com.google.maps.api.android.lib6.impl.cg.aW (:com.google.android.gms.policy_maps_core_dynamite@[email protected]:336)
at com.google.maps.api.android.lib6.impl.dm.q (:com.google.android.gms.policy_maps_core_dynamite@[email protected]:19)
at com.google.android.gms.maps.internal.p.bs (:com.google.android.gms.policy_maps_core_dynamite@[email protected]:242)
at m140.baf.onTransact (:com.google.android.gms.policy_maps_core_dynamite@[email protected]:21)
at android.os.Binder.transact (Binder.java:1352)
at com.google.android.gms.internal.maps.zza.zzJ (SourceFile:2)
at com.google.android.gms.maps.internal.zzk.onCreateView (SourceFile:5)
at com.google.android.gms.maps.zzav.onCreateView (SourceFile:7)
at com.google.android.gms.dynamic.zad.zab (SourceFile:2)
at com.google.android.gms.dynamic.DeferredLifecycleHelper.zaf (SourceFile:1)
at com.google.android.gms.dynamic.DeferredLifecycleHelper.onCreateView (SourceFile:2)
at com.google.android.gms.maps.SupportMapFragment.onCreateView (SourceFile:1)
at androidx.fragment.app.Fragment.performCreateView (SourceFile:2963)
at androidx.fragment.app.FragmentStateManager.createView (SourceFile:518)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState (SourceFile:282)
at androidx.fragment.app.FragmentStore.moveToExpectedState (SourceFile:112)
at androidx.fragment.app.FragmentManager.moveToState (SourceFile:1647)
at androidx.fragment.app.FragmentManager.dispatchStateChange (SourceFile:3128)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated (SourceFile:3072)
at androidx.fragment.app.FragmentController.dispatchActivityCreated (SourceFile:251)
at androidx.fragment.app.FragmentActivity.onStart (SourceFile:502)
at androidx.appcompat.app.AppCompatActivity.onStart (SourceFile:251)
at cgeo.geocaching.activity.AbstractActivity.onStart (SourceFile:306)
at cgeo.geocaching.activity.AbstractNavigationBarActivity.onStart (SourceFile:210)
at cgeo.geocaching.unifiedmap.UnifiedMapActivity.onStart (SourceFile:1360)
at android.app.Instrumentation.callActivityOnStart (Instrumentation.java:1712)
at android.app.Activity.performStart (Activity.java:9261)
at android.app.ActivityThread.handleStartActivity (ActivityThread.java:4422)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence (TransactionExecutor.java:282)
at android.app.servertransaction.TransactionExecutor.cycleToPath (TransactionExecutor.java:262)
at android.app.servertransaction.TransactionExecutor.executeLifecycleItem (TransactionExecutor.java:234)
at android.app.servertransaction.TransactionExecutor.executeTransactionItems (TransactionExecutor.java:110)
at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:84)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2876)
at android.os.Handler.dispatchMessage (Handler.java:107)
at android.os.Looper.loopOnce (Looper.java:249)
at android.os.Looper.loop (Looper.java:337)
at android.app.ActivityThread.main (ActivityThread.java:9403)
at java.lang.reflect.Method.invoke
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:614)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1004)
Exception io.reactivex.rxjava3.exceptions.UndeliverableException:
at io.reactivex.rxjava3.plugins.RxJavaPlugins.onError (SourceFile:372)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run (SourceFile:83)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call (SourceFile:71)
at java.util.concurrent.FutureTask.run (FutureTask.java:317)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run (ScheduledThreadPoolExecutor.java:348)
at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1156)
at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:651)
at java.lang.Thread.run (Thread.java:1119)
Caused by java.lang.OutOfMemoryError:
at io.reactivex.rxjava3.internal.schedulers.NewThreadWorker.scheduleActual (SourceFile:131)
at io.reactivex.rxjava3.internal.schedulers.ComputationScheduler$EventLoopWorker.schedule (SourceFile:229)
at io.reactivex.rxjava3.core.Scheduler$Worker$PeriodicTask.run (SourceFile:565)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run (SourceFile:80)
Exception java.lang.OutOfMemoryError:
at android.app.QueuedWork.processPendingWork (QueuedWork.java:284)
at android.app.QueuedWork.-$$Nest$smprocessPendingWork (Unknown Source)
at android.app.QueuedWork$QueuedWorkHandler.handleMessage (QueuedWork.java:312)
at android.os.Handler.dispatchMessage (Handler.java:107)
at android.os.Looper.loopOnce (Looper.java:257)
at android.os.Looper.loop (Looper.java:342)
at android.os.HandlerThread.run (HandlerThread.java:85)
If OOMEs only appeared recently, then maybe the MLKit is using too much memory? I am not aware of other recent changes with significant memory impact.
I don't think these crashes are caused by "normal" higher memory consumption - to me, this looks more like a memory leak. My observation: The longer c:geo runs, the lazier it gets - and the more likely comes the OOM-crash. Especially while routing over long distances, c:geo nearly constantly crashes at some point.
But we did not get this amount of OOM errors before on Play Store stats, it only increased over the last two (?) months (can't say exactly when, but not that long ago). As we have not changed anything in the routing code, it must be either increased size of routing data (indeed the files are larger now, but that happens gradually), or some other of the more recent changes.
Yes, maybe - but not necessarily, as a memory leak can remain undetected as long as the amount of accumulated memory remains below the hard limit. But when a memory-intensive component is added, the “base amount” increases - and from there the memory leak comes of course closer to the limit ... and possibly exceeds it ... way faster than before.
Working on #17492, I noticed that the map viewmodel geocache cache is set to a very high limit (2000 + DataStore.getAllCachesCount() -> when a user has eg 20.000 caches stored offline this would be 22.000!)
We could try to decrease this value and see what happens.
Decrease which one? The 2,000 or DataStore.getAllCachesCount()? Cause with regard to my data store 1,000 more or less will not make a big difference, as I have about 31,000 caches stored offline. :)
However, I still don't think that a constant value is responsible for the OOMEs, cause if so, the amount of memory would most probably be too high right from the start, thus c:geo wouldn't start at all ...
Decrease which one? The 2,000 or DataStore.getAllCachesCount()?
Decrease the second one :-) Maybe trying a constant of 5000 or so?
However, I still don't think that a constant value is responsible for the OOMEs...
The value we are talking about is not memory reserved on startup. It is the number of caches we will keep in memory at maximum. It is an upper limit to which caches can pule up before c:geo deletes older caches from memory. Maybe currently geocaches keep piling up in memory and OOM is reached before the max limit is reached.
But yes, this is just guessing currently. I have no hard evidence.
Decrease the second one :-) Maybe trying a constant of 5000 or so?
Worth a try. However, would be good to get some numbers regarding offline stored caches by other users who suffer from OOMEs. If e. g. some of them have only 5k or even less stored, we either need an even lower limit or the error is somewhere else ...
The value we are talking about is not memory reserved on startup
Ok, sorry, thought I saw not only a declaration but even the definition of a static list. But if not, even better, cause if it's a dynamic one with just an upper (and possibly too high) limit, this would indeed fit my "usual" OOM crashes.