Running out of memory in Glance

I have an app that capture the battery level in a background process and then adds it to an array it keeps in memory until onStop is called.This 'flat' array is a succession of three numbers (time stamp, battery level (*10 to get a decimal without wasting the space of float) and solar level).

Here's what that array looks like with just 4 'elements' in it

[1752038233,221,0,1752038271,220,0,1752038338,219,0,1752038398,217,0]

This array is read from storage in the getInitialView and getGlanceView function, lives in the app memory, gets new elements added to it in the onBackgroundData function and then put back into Storage in the onStop function. 

With time, this array grows and on my Fenix7S Pro, when it reaches just 715 of those elements, I get a Out of Memory crash while trying to store the array back into the storage from the glance process. That's just 8544 bytes (about 10K bytes according to the Memory Usage Stat) in the Simulator. The glance process isn't large by itself. The Memory Stats shows a peak of 36KB out of 60KB in the simulator using the data my device had in its storage (although the crash doesn't happen in the Simulator, just my real device)

I need that array as the app will show a graph of battery usage over time. How would you go about to prevent the Glance process from running out of memory or is this a size limit an array can be stored in Storage? I know that a background process can't pass more than around 8K through Background.exit() function but that's not what I'm doing. Through the .exit function, I might have an overnight of data pushed through its onBackgroundData function, which would be around 1KB of data. It's just when I store back that array from the Glance process that I get the Out of Memory crash.

Thanks.

  • I assume the background service gets the battery level at regular intervals so you can cut your array size in half by not storing the timestamp for each sample.  You only need the time for the first sample which you keep updated with simple math

  • As to why it runs out of memory, my guess is that during the serialization process of Storage.set/getValue, there is a good deal of overhead, especially if serialization is to/from JSON or XML

  • When you have some code with the glance annotation and the glance runs, code for the background annotation also gets loaded so that can impact your available memory,  Keep both as small as possible.  And be careful with globals as they'll eat up memory too.

  • On some devices when running a CIQ device app, the background service doesn't run, so if you want the battery level, you might not see anything the whole time you are running the device app.

    I have a similar app for battery level, where I include the "unix time" of a reading  so when display a graph I can show when a gap occurs and how long that gap is.

  • As to why it runs out of memory, my guess is that during the serialization process of Storage.set/getValue, there is a good deal of overhead, especially if serialization is to/from JSON or XML

    I've also found that the Storage module is generally memory-intensive, and this becomes especially problematic in glances due to their tighter memory constraints. Even relatively small data structures — particularly dictionaries — can push a glance over its limit.

    A critical issue I’ve encountered and documented: when a Storage.setValue() call in one glance fails due to memory exhaustion, it can crash another, completely unrelated glance. This suggests that for some purposes, glances may share a common memory pool.

    I believe Storage should follow the lead of Communications by handling memory exhaustion more gracefully — with a catchable exception or a clear failure result.

    I've submitted a bug report and suggestion here:

    forums.garmin.com/.../storage-setvalue-should-handle-memory-limits-gracefully

  • "Keys and values are limited to 8 KB each, and a total of 128 KB of storage is available"

    That would explain why it crashes after 8K. So to be safe, I could use multiple (up to 16) arrays to store over 8K of battery level history. It does complicate things a bit but should be workable. 

    I upvoted your bug report. Indeed, it should handle it more gracefully than crashing.

  • So I've been running the new code for a few days and so far so good. Instead of having a huge array holding the time stamp, battery level and solar intensity (if equipped), I have five arrays of 500 elements each (an element is these two/three fields above). I also have another array (HISTORY_ARRAY ) holding the time stamp of the first entry and each HISTORY array is saved as HISTORY_timestamp. So through the HISTORY_ARRAY array, I can easily access any HISTORY array (for building the graph)  and the latest entry is the most current one (using the .add() operator, it's always the last one and when it's full, I simple remove the first element.

    The slopes calculations was also very CPU intensive so to alleviate this, I have five more arrays named SLOPES_timestamp that contains the calculated slopes of the corresponding HISTORY_timestamp array. So they are only calculated once and used thereafter. Because all the slopes are already calculated (except for the most current history where the slope of the latest down trend isn't yet known and is calculated), I don't need to load much data in memory while the glance is running and the transfer of the new background data is only happening on an array of at most 500 elements (about 6K bytes), it hasn't crashed yet.

    Beside the history arrays, the others are quite small and to prevent the overhead of the .add() operator on that 6K array (ie, doubling in size as it copies from the old one to the new one), I simply create it at full size in advance and simply store the values at their corresponding slots.