Memory out of range

Hello..
I need to store n number of objects in an array.

in this case i have array

x=[]

after every 10 second i want to add object for x

y={"step"=>10}

x.add(y)

but after around 20  minutes app crashed and show Memory out of range What can I do for fix that

 
  • When you do something like x.add(y) is that memory gets allocated for a "larger x", then what's in the original X gets copied to the "larger x", so during that time, you are using about double the memory used for x.  For a large array, and based on available memory, you could run out of memory.  

    What you may want to consider is allocating x to it's max size, and managing what's in it yourself.  So for example, say you set x to have 1000 elements, and you keep track of the last one you used. and then handle the case where all 1000 are used.  This avoids the "double memory" when you do an add()

  • Even if you avoid using add() by preallocating your array as suggested, dictionaries still take up a lot of memory. In general, it's better to have a flat array of "primitives" (*) as opposed to an array of objects. (* For this purpose, Number, Float, Boolean, String and null are primitives, but Long and Double are not primitives.)

    Given this example code:

    y={"step"=>10}

    Can y ever have any fields besides "step"? If not, it would make more sense for x to be an array of numbers.

    e.g.

    More memory: x = [{"step" => 10}, {"step" => 20}, {"step" => 30}];

    Less memory: steps = [10, 20, 30];

    On other hand, let's say that y can only ever have up to a fixed number of fields (say 3: steps, HR, cadence). In this case, it would be better to have 3 flat arrays.

    e.g.

    More memory:

    x = [
      {"step" => 10, "hr" => 149, "cadence" => 185},
      {"step" => 20, "cadence" => 190},
      {"step" => 30, "hr" => 148, "cadence" => 184}
    ];

    Less memory:

    steps = [10, 20, 30];
    hr = [149, null, 148];
    cadence = [185, 190, 184];

  • Unless it would not work for your, app I agree with FlowState and wouldn't use a dictionary.

    And if you are talking "steps" as what happens when you walk, you probably won't see them change every 10 seconds.  You will see this on a real device but not in the sim.  On a real device, things also happen where you stop for a while and "false step" - like just moving your arm -aren't counted. 

    The simple approach is don't add them every 10 seconds- maybe 20,30, or 60 seconds

    The more complex approach, is only add them when they change.  In that case, I'd probably add a timestamp with the value, so you can detect large gaps, like when you are sitting for a while.  I use Time.now().value() for this..

    edit:

    To see how this works on a real device, my Hike2+ app has a screen that shows steps..  For the activity, daily, and goal.  By default, it updates the screen every second.

    https://apps.garmin.com/apps/116a5b59-29ae-4397-a70e-907d7e5f8e44

  • There is only 20x 60/10 = 120 elements in array, a bit little to get out of memory error, unless app alone almost fill memory.

    Error is out of range so probably bug is in code when reading data from array.

  • Yeah, I would tend to agree with this. The actual error message for accessing a non-existent array index is "Array out of bounds error" tho, while the error for running out of memory is "Out of memory error". OP's report of "memory out of range" does sound closer to the first one, in a way.

    It would help if OP could post the exact error they're getting.

  • Thank you...

    exact error is.

    Error: Out Of Memory Error

    Details: Failed invoking <symbol>

  • Actually i saved 

    {
            "index": 0,
            "startTimeUtc": "2024-06-30T01:10:17.312Z",
            "endTimeUtc": "2024-06-30T01:10:17.312Z",
            "startLocation": {
              "accuracy": 0,
              "altitude": 0,
              "altitudeAccuracy": 0,
              "heading": 0,
              "latitude": 0,
              "longitude": 0,
              "speed": 0
            },
            "endLocation": {
              "accuracy": 0,
              "altitude": 0,
              "altitudeAccuracy": 0,
              "heading": 0,
              "latitude": 0,
              "longitude": 0,
              "speed": 0
            },
            "steps": 0
          } this kind of object


     

  • Thanks for clarifying!

    Hmm there is a huge amount of overhead in that data structure, due to the use of dictionaries, string keys and string values. You could probably save a lot of memory by using symbols for keys, but you will still get maximum savings by flattening the object into a number of arrays as suggested.

    Memory usage of object (as in example above)

    As a test, I created the following object as a global variable in a CIQ app and looked at its size in the memory viewer:

    var g_data = {
        "index" => 0,
        "startTimeUtc" => "2024-06-30T01:10:17.312Z",
        "endTimeUtc" => "2024-06-30T01:10:17.312Z",
        "startLocation" => {
            "accuracy" => 0,
            "altitude" => 0,
            "altitudeAccuracy" => 0,
            "heading" => 0,
            "latitude" => 0,
            "longitude" => 0,
            "speed" => 0
        },
        "endLocation" => {
            "accuracy" => 0,
            "altitude" => 0,
            "altitudeAccuracy" => 0,
            "heading" => 0,
            "latitude" => 0,
            "longitude" => 0,
            "speed" => 0
        },
        "steps" => 0
    };
    

    It's 1082 bytes, so it's not surprising you're running out of memory. (120 * 1082 = 129,840)

    In addition to using flat arrays, I would suggest storing UTC timestamps as number of seconds since the Unix epoch (so each one fits in a single Number.) If you need milliseconds, that can be stored as a 2nd number.

    Memory usage of flat arrays (single element)

    Here's an example of everything as arrays (no strings), with 1 element each (as a baseline):

    // idk where "2024-06-30T01:10:17.312Z" comes from, but
    // lets assume it was parsed by other code into separate elements
    var startTimeSeconds = [
        Gregorian.moment(
            { :year => 2024, :month => 06, :day => 30, :hour => 1, :minute => 10, :seconds => 17 }
        ).value() // converts the given timestamp into # of seconds since the unix epoch
        // unix epoch = 00:00 Jan 1 1970 UTC
    ];
    // we store timestamp milliseconds separately
    var startTimeMilliseconds = [
        312
    ];
    var endTimeSeconds = [
        Gregorian.moment(
            { :year => 2024, :month => 06, :day => 30, :hour => 1, :minute => 10, :seconds => 17 }
        ).value()
    ];
    var endTimeMilliseconds = [
        312
    ];
    var startAccuracy = [ 0 ];
    var startAltitude = [ 0 ];
    var startAltitudeAccuracy = [ 0 ];
    var startHeading = [ 0 ];
    var startLatitude = [ 0 ];
    var startLongitude = [ 0 ];
    var startSpeed = [ 0.0 ];
    
    var endAccuracy = [ 0 ];
    var endAltitude = [ 0 ];
    var endAltitudeAccuracy = [ 0 ];
    var endHeading = [ 0 ];
    var endLatitude = [ 0 ];
    var endLongitude = [ 0 ];
    var endSpeed = [ 0.0 ];
    
    var steps = [ 0 ];

    This takes up 380 bytes (20 bytes per array, 19 arrays), which still sounds like a lot, but each array actually has 15 bytes of fixed overhead. Each element takes up 1 + 4 bytes (if you stick with Numbers / Floats / null, and avoid Longs, Doubles, Strings and objects). (The extra byte is for the runtime type.)

    Memory usage of flat arrays (multiple elements)

    Let's add a 2nd element to each array (to confirm what was said above):

    var startTimeSeconds = [
        Gregorian.moment(
            { :year => 2024, :month => 06, :day => 30, :hour => 1, :minute => 10, :seconds => 17 }
        ).value(),
        Gregorian.moment(
            { :year => 2024, :month => 06, :day => 30, :hour => 1, :minute => 10, :seconds => 17 }
        ).value()
    ];
    var startTimeMilliseconds = [
        312,
        312
    ];
    var endTimeSeconds = [
        Gregorian.moment(
            { :year => 2024, :month => 06, :day => 30, :hour => 1, :minute => 10, :seconds => 17 }
        ).value(),
        Gregorian.moment(
            { :year => 2024, :month => 06, :day => 30, :hour => 1, :minute => 10, :seconds => 17 }
        ).value()
    ];
    var endTimeMilliseconds = [
        312,
        312
    ];
    var startAccuracy = [ 0, 0 ];
    var startAltitude = [ 0, 0 ];
    var startAltitudeAccuracy = [ 0, 0 ];
    var startHeading = [ 0, 0 ];
    var startLatitude = [ 0, 0 ];
    var startLongitude = [ 0, 0 ];
    var startSpeed = [ 0.0, 0.0 ];
    
    var endAccuracy = [ 0, 0 ];
    var endAltitude = [ 0, 0 ];
    var endAltitudeAccuracy = [ 0, 0 ];
    var endHeading = [ 0, 0 ];
    var endLatitude = [ 0, 0 ];
    var endLongitude = [ 0, 0 ];
    var endSpeed = [ 0.0, 0.0 ];
    
    var steps = [ 0, 0 ];

    Now each array is 25 bytes.

    So your total memory usage with 19 flat arrays of numbers and floats is 19 * (15 + num_elements * 5). For 120 elements, that's 11,685 bytes. Still sounds like a lot, but it's roughly 1/10th of 129,840 (a whopping 90% memory savings).

    Ofc if you need to serialize/deserialize any of this stuff to/from the network, that's additional work. But clearly it's not practical to store all of that data in your app as-is.