Out of memory error when it makes no sense

I got these errors in ERA:

Error Name: Out Of Memory Error
Occurrences: 4
First Occurrence: 2022-12-12
Last Occurrence: 2023-01-04
Devices:
fēnix® 6 Pro / 6 Sapphire / 6 Pro Solar / 6 Pro Dual Power / quatix® 6: 23.10
fēnix® 6X Pro / 6X Sapphire / 6X Pro Solar / tactix® Delta Sapphire / Delta Solar / Delta Solar - Ballistics Edition / quatix® 6X / 6X Solar / 6X Dual Power: 23.10
 fēnix® 7X / tactix® 7 / quatix® 7X Solar / EnduroTm 2: 10.44
App Versions: 2.1.0
Languages: pol, slo, spa
Backtrace:
FitContributor.initialize:58
Field.initialize:602
MyApp.initialize:23

from the stacktrace I see that this is the very start of the app, when each object is created, but nothing more happens*, so in every device the same amount of memory should be allocated (no user input or preferences are read), and since I know that the app works on real fenix6 this makes no sense for fenix6pro, and even less for fenix7 that has 256K DF memory (as opposed to 32K for f6) to run out of memory at this point.

*) The only code that runs before this point that somehow could have different effect on different devices is this:
GenericChannel.initialize(method(:onMessage), new Ant.ChannelAssignment(Ant.CHANNEL_TYPE_RX_NOT_TX, Ant.NETWORK_PLUS));

What can cause this? Is it possible that the device starts the DF even though it has not enough memory to give it the amount "promised" by Garmin?

class MyApp extends Application.AppBase {
	var mSensor as Sensor;
    var mField as Field;
    function initialize() {
        var sensor = new Sensor();
        mSensor = sensor;
        mField = new Field(sensor);
    }
}

class Sensor extends Ant.GenericChannel {
    const DEVICE_TYPE = 0x78; // Heart Rate Sensors - 120

    // PERIODS: 4Hz: 8070, 2Hz: 16140, 1Hz: 32280
    const PERIOD = 32280;

    public var deviceCfg as DeviceConfig; 
    public var data as Data;
    public var searching as Number? = null;

    public function initialize() {
        try {
            GenericChannel.initialize(method(:onMessage), new Ant.ChannelAssignment( // removing "Ant." saves 4 bytes, but adds warning
                Ant.CHANNEL_TYPE_RX_NOT_TX, // Bidirectional Receive (Slave)
                Ant.NETWORK_PLUS)
            );
        } catch(e instanceof Ant.UnableToAcquireChannelException) {
            logRelease(e.getErrorMessage() as String);
            searching = -1;
        }

        data = new Data(); // doesn't even have initialize(), just a few class variables

        // Set the configuration
        deviceCfg = new Ant.DeviceConfig( {
            :deviceNumber => -1,                   // Set to 0 to use wildcard search
            :deviceType => DEVICE_TYPE,            // Heart Rate Sensors: 0x78 = 120
            :transmissionType => 0, // Set to 0 to use wildcard search
            :messagePeriod => PERIOD,              // 4Hz: 8070, 2Hz: 16140, 1Hz: 32280
            :radioFrequency => 57,                 // Ant+ Frequency 2.457GHz
            :searchTimeoutLowPriority => 10,       // Timeout in 25s
            :searchThreshold => 0} );              // Pair to all transmitting sensors
    }
}

class Field extends WatchUi.DataField {
    protected var mSensor as Sensor;
    public function initialize(sensor as Sensor) {
        mSensor = sensor;
        mFitContributor = new FitContributor(self);
        handleSettingUpdate();
    }
}

class FitContributor {
    (:typecheck(false))
    public function initialize(dataField as DataField) {
        var bpm = "bpm";
        var heart_rate = "ANT_HR";
        mCurrentHRField        = dataField.createField(heart_rate,       CURR_HR_FIELD_ID,      Fit.DATA_TYPE_UINT8,  { :mesgType=>Fit.MESG_TYPE_RECORD,  :units=>bpm});
    }
}
  • This probably won't help, but you could always try calling System.getSystemStats() and displaying totalMemory, freeMemory and usedMemory before the line of code that crashes. I don't think it's possible for the firmware to give the app less memory than it's supposed to, tho.

  • No it doesn't really help for multiple reasons:

    1. we have no way to see logs from ERAViewer Disappointed

    2. adding code for debugging when the problem is already not enough memory is maybe a good idea for debugging locally, but bad idea when you have to release it as production code.

    I did use that trick when I had some problem locally and the only thing I could learn from it that it takes illogically too much memory to call getProperty/getValue on dictionaries and it was better to avoid them even if it's lesser user experience (the settings screen)

  • How complex are your app settings?  If I recall, when an app starts, they all get loaded into memory, and if I recall, the max size for everything is something like 8k.  Say the default values are small, but if they can be larger (a short string by default, but the user sets a much longer string).  With Storage, you can have something like 8k per key, but it's not the same for Properties where it's 8k total.

    That could be why you're getting the out of memory when the app is just starting up.

  • Even that can only explain it n fenix6, but there's no way that all the settings having the longest possible value reach 256 KB on fenix7

  • 8k - not 8kb.  You are thinking total app size, not the max setting size.

  • We're programmers here, not marketing gurus. I don't know what 8k means. Either 8000 or 8192. But that's a number. What is the unit? bit? byte? word (32 bit)? number of property keys? It can't be anythig but bytes, so it's 8kB, and even if it means 8192 it's way more than all my settings having the longest value can be. I have 11 numbers (0-1048576), 2 boolean, and 9 strings (the longest with maxLength:32) so all those IMHO can be no longer than 11*7 + 2 + 9*32 = 367. Even if we add 8 extra bytes for each it won't be more than 543. So I added MyApp.mWasteMemory = new Array(140) which allocates 140*4 bytes, and it still works with fenix7.

  • Tbh, I would've had the exact same reaction, but I think it was a typo and he meant "8k - not 256 kb". I still agree with your comment 100% tho.

    I think he's trying to say that he thinks your app is running out of memory because it's hitting an 8192-ish byte properties limit, and not the 256 KB total app memory limit.

    My gut feeling (which is often wrong) is that it doesn't sound right to me, but I'm not sure. My reasoning:

    - Why would the system wait until FitContributor.initialize() to throw the Out of Memory error instead of throwing it at the very start, if it's hitting a hard properties size limit?

    - Would the system really throw "Out of Memory" if it was only hitting a properties memory limit or would it throw a different error? (To be fair, I've never seen this problem so I wouldn't know.)

    I created a single string property with a length of ~10000 bytes in a CIQ data field and it worked in the simulator for the fenix 7 system 6 preview device, but I have no idea what would happen on a real device.

  • To get a feel for how large settings are, look on a device in the garmin\apps\settings folder for the .set file.

    And the 8k is a max.  Not a constant.  If the code and data itself if 25kb, and the max size is 28kb (as is the case for data fields on a number of devices (32kb-4kb)), it's only got 3k when loading settings.  Even if the 8k settings still fit, the next time you use a bit more memory in the app, you could get the out-of-memory.

  • There are a number of mentions of memory issues and settings over the years in this forum.  Most are changing settings while and app is running.  The old object store also had a max of 8kb, BTW.

  • hi, is there a way to fint it with the sim?

    I had a look here: C:\Users\XXXXX\AppData\Local\Temp\GARMIN\APPS\SETTINGS

    but always empty.

    or is it the diff with peak on sim before and after you apply a setting?