exitTo() Out Of Memory Error

My yacht racing app is too large to run in 124kB, so, I have split it into two components: STARTING , which is operational in the period up to the starting gun, then RACING takes over for the actual race.

I use exitTo() to link the two components.

In testing, the RACING app crashes with out Of Memory Error when it is invoked from STARTING, even though there is plenty of memory for the individual components run independently. RACING runs independently with Peak Memory of 102 Kb.

However the crash is intermittent: ~90% on the device and ~10% in the simulator (videolink here ). I have of course been at pains to ensure that the the test environments are identical.

I rebooted the Mac and ran in the same manner - with no crash. (VIDEO HERE ) showing the second app running correctly with a PEAK memory of 105Kb.
I can't show the console output as it's the exited to app that's crashing, but when I run it on a physical device I get:
(VA-HR)
ERROR: Out Of Memory Error
DETAILS: Failed invoking <symbol>
STORE_ID: 00000000000000000000000000000000
CALLSTACK:
@PC = 0x30000dad
@PC = 0x30000e64
/Users/.../Documents/watchApp/raceQs_Racing/source/main.mc (onStart:27)

(F5)
---
Error: Out Of Memory Error
Details: 'Failed invoking <symbol>'
Time: 2019-01-10T02:31:55Z
Part-Number: 006-B2697-00
Firmware-Version: '12.00'
Language-Code: eng
ConnectIQ-Version: 3.0.7
Filename: QS_RACE
Appname: QsPlus
Stack:
- pc: 0x30000f9f


It seems to me that there may be a latency issue: the first app has not completely cleared memory before the second app is loaded.

I can't think of any way to delay the loading of RACING since I lose control on the execution of exitTo(). Any ideas?
  • You can free memory setting unused vars to null.

    Perhaps you can win some time setting global objects to null before the exitTo() ...
  • Former Member
    Former Member over 6 years ago
    What you are implying isn't really possible since applications do not share their memory pools in any way. Allocations from one application cannot impact the memory usage of another.

    You might want to make sure the data being passed from the exiting application to the new application isn't larger than you expect it to be. This is the main difference you would have between starting it on its own, and exiting to it.
  • Allocations from one application cannot impact the memory usage of another.

    THat's very encouraging
    You might want to make sure the data being passed from the exiting application to the new application isn't larger than you expect it to be. This is the main difference you would have between starting it on its own, and exiting to it.

    OK thanks, I'll investigate.
    I am passing a single but fairly complex Dictionary.


  • Brian.ConnectIQ I have sacrificed some functionality and reduced the size of the passed Dictionary which appears to have resolved the problem.
    The receiving app is now reporting a Peak Memory of 90 Kb.
    I am still concerned that I may have had to sacrifice the functionality due to a deeper issue.
    When I took a very coarse look at the size of the dictionary ( with .toString().length()) it reported around 12 Kb, but that would have been an inflated figure as the vast majority of the variables were integers.

    So it hasn't really answered two questions:
    • When it ran successfully on the sim, the receiving app was using 105 Kb which is ~10Kb under the limit and it was passing the same Dictionary as when it failed. When I run the receiving app directly, the Peak Memory is 89 Kb.
      Why does a 105Kb app generate an Out Of Memory Error?
    • The crash, on the sim and on both devices was intermittent. Why intermittent?
  • Sorry to jump in here, but a dictionary uses much more memory than the string that is used to represent it. For one thing, with a dictionary you can instantly look up values by keys, which is impossible with a single flat string, so it's clearly a more complicated structure. For another, each of the key strings has the overhead associated with strings -- the smallest string in CIQ still takes up 10 bytes or something, IIRC. Even if all your keys were one character, there's still a ton of overhead.

    Integer values displayed in the string could also take up less space than the actual size of the values in the dictionary if they are on average less than 5 digits + whatever the overhead is for values in a dictionary. This is because in Monkey C, simple objects ("primitives", like integers and floats) take exactly 5 bytes afaik: 1 byte for the type field, and 4 bytes for the data.

    If you want to get a good idea of the memory that is taken up by that dictionary, I would create it in a test app (you could just hardcode the dictionary in code) and look at the size in the debugger. I would bet that it's a lot bigger than 12 KB.

    Depending on how you are storing things internally, it might make sense to just pass things as an array, if you run out of space. Or even as a string of comma separated values (or whatever) -- anything that can easily be parsed with a minimum of code on the receiving end. It all depends on how you access that data later.
  • FlowState Please, don't apologise, your insights are gold.
    I hear what you say about memory use of Dictionaries, but I wonder if there's a programmatical way of determining the size - observing in the debugger is soo clunky.
    And yes, I may well save memory by casting my data as an array, but in fact, the main space component in this particular dictionary (which I have had to abandon to work around the crash) is a 1000 element array of numbers, so your suggestion may not yield the expected return.
    I still suspect there['s another force at play causing the crash.
    Maybe, as has been suggested on a previous issue, it's a problem with memory allocation in CIQ - unable to find a big enough contiguous space? After all, the error is intermittent!
  • Former Member
    Former Member over 6 years ago
    I don't really think I would be able to answer the questions you are asking unless I very familiar with how your application works, and the structure of its active memory. The best I can provide is random speculation about the types of things that could create your symptoms.

    How can you get "Out Of Memory Error" when you a below the memory limit?
    - A large allocation is attempted when your application memory is irrecoverably fragmented.
    The dictionary you are passing is probably quite large if it is 12kb when stringified.
    You should look at how large this dictionary actually is by viewing it in the memory viewer.
    Passing this as a flat structure to save space (Possibly an Array), and unpacking it into multiple objects could reduce fragmentation load.
    - A state change in your application allocates a large amount of memory, and the peak is much lower before it happens

    Why is it random?
    - Fragmentation could behave differently depending on the exact state of your application. A large locked element could make defragging very difficult.
    - A fluke in the exiting application could send larger data than normal, or malformed data that causes an unexpected code path.
  • I was just going to mention the memory viewer in the sim, but Brian beat me to it! You can see a whole bunch about the memory used for various things, in # of bytes, etc.

    When I have something like a large array, I do a "new [MAXSIZE]" and don't grow it. I suspect that doing something like an add(), means memory is obtained for the new size, the old data is copied, and then old memory is then freed, so two large chunks are need at the same time, and if there's fragmentation, that could be an issue, and more so as the app runs and the array keeps getting larger. And it would be random.
  • Thanks all for updating me on ways to assess memory usage of variables.

    Sure, the Dictionary is big('ish), but it's the same size in all the tests on the different platforms.
    In all scenarios, the calling app has only been running for a short period of maybe 2 minutes, and the receiving app is crashing immediately: before I can get a System.println out of onStart of the main module.
    There's not much to be known about the apps: the sending app is building a Dictionary of relevant state variables and exiting to a receiving app which is crashing in onStart().

    My work around is based on an assumption that the Dictionary size is the issue, but if that turns out to be an incorrect assumption, then I may have a major support issue when the apps are published and deployed with real users.

    So, my underlying question is why does it crash almost every time on the F5/VAHR devices but very, very rarely on the sim?

  • Sorry, when I said “look at size in debugger”, I meant “look at size in simulator memory viewer” >_>. Jim and Brian beat me to it anyway. And the comments about fragmentation are pretty relevant, especially for large allocations.