Out Of Memory Error on dictionary declaration

While working on my very first Garmin project I'm getting this error Error: Out Of Memory Error
Details: Failed invoking <symbol>

I think this happens because I'm trying to declare a dictionary from an extreamly long string. It has more than 5500 characters. I generated this string from a json file using python because I want to make my data field run on a forerunner 235 and I found out that this device can load resources because of the limited api level. So I decided to just generate an .mc file with the array in it. All looks good though.

Why can't I create a dictionary like this? Is there an other way to get this data into my app? 

  • You won't get much cheaper memory wise than flat arrays with floats.  16k is only a tiny amount of memory, and you'll use some of that with your AppNBase and other basic stuff.  In my DF that saves locations, that's really about all it does, except to display the breadcrumb trail.

  • I tried using a property string but parsing the string takes again to much memory. I came up with this idea. Adding all locations as their own property. Is that a good idea? So I can do something like this:

    for(var i = 0; i < 114; i++)

    {

    tempLoc = App.getApp().getProperty(string(i));

    dist = distance(tempLoc, currentLoc);

    }

  • Strings use more memory than floats A float is a simple object while a string isn't.  You can't get cheaper than a flat array with floats.

    Look at reducing memory in your code, and one thing to look at is using dc calls instead of layouts.

  • Would generating an array from a long comma separated string in the end save memory? I don't really get how that saved anything since you have to load it in eventually?

    It depends on whether the memory used for the string (and parsing) the string is less than the amount of the memory for the code to construct the array.

    It's the same idea behind JSON resources. You could ask how JSON resources save memory since you have to load them in anyway.

    But when you have a large data structure in code (say a big array), there's two memory hits:

    1) The memory taken up by the object at run-time (this amount can change to 0 if you free the memory at some point)

    2) The memory taken up by the code to construct the object (this amount stays the same for the lifetime of your app)

    By moving large data to a JSON resource, you eliminate 2).

    If you don't have JSON resources available, then if you try a trick such as encoding the data a string and putting it in a property, you eliminate 2), but then you use up memory loading the string and parsing it (including the code to do so).

    For example, say I add the following array to the view class of my data field:

    var dataArray = [1,2,3,4,5,6,7,8,9,10];

    This adds 12+65 bytes of memory for the array itself, but it also adds 144 (!) bytes of code (to construct the array). That's a ~200 byte hit for a 65 byte array.

    If I could load that array from a JSON resource, I should be able to save that 144 bytes of code, at the cost of the size of the code to load the array, plus whatever memory spike happens when the load occurs.

  • Adding all locations as their own property. Is that a good idea?

    Properties are loaded into your app's memory as a dictionary. The more properties (and string resources) you have, the more memory will be consumed at run time. (In this case, it's not the values of the properties that matter, just the length of the property names and the number of properties. For example, if you have a property whose value is really long string, the string itself won't take up memory until you load it.)

    You could try that but then I think you'd still have the problem of doubling up your memory, assuming you tried to load all the locations into memory once. If you only load one location at a time, it could work.

  • Actually, Properties are different than Storage in how they work.  Properties are loaded into memory, and you can see the impact when onSettingsChange is called.  Plus on a 235, you'll load them with Application.getApp().getProperty()

  • Actually, Properties are different than Storage in how they work.  Properties are loaded into memory, and you can see the impact when onSettingsChange is called.  Plus on a 235, you'll load them with Application.getApp().getProperty()

    Sorry, I was thinking of string resources -- my bad.

    It's resources (not properties) where only their number (and not their values) impacts run time memory (assuming you load at least one resource into your app.) (If you never load a resource into your app, then the number of resources won't impact run time memory.)

    The workaround I employed for devices without JSON resources was to use a string *resource* not a string property.

    e.g. strings.xml:
    <strings>
        <string id="locationData">Location1,1.234,2.3456,Location2,...</string>
    </strings>

    Code:

    var locationDataString = WatchUi.loadResource(Rez.Strings.locationData);

    var locationData = parseLocationData(locationDataString); // you have to write parseLocationData()

  • Loading a single string from a string resource and then parsing it wouldn't that throw a out of memory error again while trying to parse the string since you need to have the whole thing in memory to?

    I've a loop in my code to see which of the locations is the closest so I don't need all the locations at the same time. Wouldn't it be more efficient to just add the locations each as a separate string so it only needs to parse a small string?

    <strings>
        <string id="locationData0">Location1,1.234,2.3456</string>

        <string id="locationData1">Location6,2.234,2.3456,</string>

        <string id="locationData2">Location1,5.234,2.3456,</string>

    </strings>

    So this is also loaded 

    Edit: Tested this both with a single string and multiple, no luck. Out of memory on app load. Resource strings are also loaded at the same time I guess.

  • Loading a single string from a string resource and then parsing it wouldn't that throw a out of memory error again while trying to parse the string since you need to have the whole thing in memory to?

    I've a loop in my code to see which of the locations is the closest so I don't need all the locations at the same time. Wouldn't it be more efficient to just add the locations each as a separate string so it only needs to parse a small string?

    <strings>
        <string id="locationData0">Location1,1.234,2.3456</string>

        <string id="locationData1">Location6,2.234,2.3456,</string>

        <string id="locationData2">Location1,5.234,2.3456,</string>

    </strings>

    So this is also loaded 

    Edit: Tested this both with a single string and multiple, no luck. Out of memory on app load. Resource strings are also loaded at the same time I guess.

    I tried simulating it on a vivoactive3 and all options work find on it. The flat list is the best solution since the peak memory was 22.9kb for every different method I tried. I did not bother parsing the strings since they already caused an out of memory error anyway. The constant memory usages is 15.2 so maybe optimising the code bit clears some kb that I need for loading in my data. I'll look into the view/dc and see if I can scrape off some kb's there. 

  • The va3 has 28k for data fields, so much more than the 16k on a 235.

    The number you want to watch is "peak memory". That's what has to stay under the limit.