Overview
Utilizing memory as efficiently as possible is a core requirement for making the Sedona Framework run on small, embedded devices. We divide memory into the following sections:
- Stack: the call stack (RAM)
- Data: all the memory declared as static fields in the current scode (RAM)
- Sedona Framework App: the components that define the current application (RAM)
- Sedona Framework Code: the scode image for the installed kits (RAM, or possibly flash memory)
- SAB File: the compact binary representation of the application (usually stored in flash)
- C Code: native code and SVM executable (RAM, or possibly flash)
- C Data: native code data segment (RAM)
- C Stack: the C native call stack (RAM)
We use the terms RAM and flash a bit loosely here: RAM must be high speed read/write memory, and flash is persistent memory (on a large device this could be a disk drive). If flash is high speed you might be able to run code directly out of flash, but normally those code segments will be loaded into RAM.
While reading this topic you may find it useful to refer to the Sedona VM
source code, located in the src/vm
folder.
Computing Memory Requirements
So how do you figure out how much memory each of the sections declared above requires? Let's look at each section:
C Code, C Data, and C Stack
The C code, data, and stack segments will be determined by your development tools and C compiler. We use the term "C Code" to denote any non-Sedona code such as C, C++, or assembly. This code typically includes boot code, OS code (if your device has one), all your native method implementations, comm stacks, and the SVM itself. All this is normally lumped into a single binary executable file called "svm". The C Data segment includes all the memory buffers used by this code.
Note: On some platforms, part of the SVM may already be in ROM or other permanent storage. In that case the "svm" executable created during compilation will contain only the portion of the SVM that is not in ROM, and its RAM footprint will be reduced.
Stack
The Sedona stack is defined by the stackBaseAddr
and stackMaxSize
fields of the SedonaVM
struct when the VM is launched.
Typically the size is one or two KB - you may wish to test
your application to find the maximum call stack. Heavy use of recursion and
methods with a large number of locals will affect your call stack size.
Code and Data
The sizes of the Sedona Code and Data segments are calculated when the scode image is compiled:
D:\sedona>sedonac scode\x86-test.xml ReadKits [5 kits] WriteImage [D:\sedona\scode\x86-test.scode] (29632 bytes) +---------------------------------- | Data: 0.4kb (360 bytes) | Code: 28.9kb (29632 bytes) | Total: 29.3kb (29992 bytes) +----------------------------------
These numbers are directly related to how many kits
you include and the code size of those kits.
Declaring static fields will consume memory in your Data segment;
instance fields only affect the application size.
If you are running your code out of RAM, then the Total size is what matters.
If you can run your code out of flash, then the Code will not use any RAM
but the Data segment must still be stored in RAM.
Note that the scode flags test
and debug
can have a huge impact on scode size.
The location of the Code is passed to the VM in the SedonaVM
struct via the codeBaseAddr
field. The Data section
is allocated by the malloc
macro - see "sedona.h" for details.
Sedona Framework Application
The Sedona Framework application itself is always run out of RAM. This is where we
instantiate the components and links. Memory for components and links is allocated
by the malloc
macro. If components or links are removed
during runtime, then the memory is freed by the free
macro
(if the platform supports it).
The Sedona Framework application runs out of RAM, but has configuration data that
must be persistent between power cycles. So we also store the
application to flash as a SAB file. How
this file is loaded on startup and stored back to flash on save is handled
by the platform's implementation of the sys::FileStore
native methods.
Sedonac will report how much RAM and flash a given application consumes when you do a conversion between SAX or SAB:
D:\sedona>sedonac apps\test.sax ConvertAppFile [D:\sedona\apps\test.sax -> D:\sedona\apps\test.sab] +---------------------------------- | RAM: 14.2kb (14588 bytes) | FLASH: 0.4kb (382 bytes) +----------------------------------
See the Field Layout section to evaluate how much memory each component type consumes.
Heap
The memory for the Data section and for the
components and links in the App is allocated using the malloc
macro as defined in "sedona.h". If components or links are removed
during runtime, then this memory is freed by the free
macro.
You can implement your "heap management" using three strategies:
- Stdlib: if you have the resources, you can just use C's built-in malloc and free. However if using a compiler like GCC this might require importing a huge chunk of library code into your native image.
- Custom Heap: if the stdlib implementation of malloc and free is too big, you might consider writing your own simple heap manager.
- Malloc Only: many devices might require only a static application in which case there is no requirement for freeing memory. In resource limited devices, the ability to free memory might be outweighed by the risk of heap fragmentation. In this case your heap management might be nothing more than a pointer to the next chunk of memory to allocate. A call to free would be a no-op. If objects are removed from the application the memory would not be recovered until the system is reset.
Field Layout
You can pass the -layout
flag to sedonac when compiling
your scode image to dump the memory layout of each type. This
gives you exact details for how memory is being consumed:
- How many bytes each component type consumes in RAM
- Memory offset of every instance field against the object's base address
- Memory address of every static field against the Data base address
A good rule of thumb is that each component averages between 50 and 100
bytes and each link consumes 16 bytes. A network protocol service
will typically consume several KB since it must allocate buffers
and internal data structures. However, you really must use the
-layout
flag to see exactly how many bytes each component
will consume.
Note that sedonac has no visibility into the behavior of native methods.
Any dynamic memory allocation that occurs at the native level must be
calculated separately and added manually to the totals provided by
the -layout
flag.
Hibernation
The Sedona Framework supports hibernation, which allows a device to enter
a low power state. The Sedona Framework assumes that during hibernation its data section
in RAM (managed by the heap malloc
and free
calls) will not be affected. The device can either maintain RAM
during hibernation or save and restore to the same memory addresses.
See Hibernation.