Optimizing RAM Memory Demand

In many embedded applications, the amount of RAM memory required is even more important than the application performance and its codesize. Therefore, a number of means to control the applications RAM requirements are available in Jamaica.

RAM memory is required for three main purposes:

  1. Memory for the application's data structures such as objects or arrays allocated during runtime.

  2. Memory required to store internal data of the VM such as representation of classes, methods, method tables, etc.

  3. Memory required for each thread such as Java and C stacks.

Measuring RAM requirements

The amount of RAM memory required by an application can be determined by setting option -analyse 1, as in this example:

	>jamaica -smart -analyse 1 CaffeineMarkEmbeddedApp 
-destination caffeine_smart_analyse
		  
Jamaica Builder Tool 2.2 Release 1
Generating code for target 'linux-gnu-i686'
 + caffeine_smart_analyse__.c
 * C compiling
 * linking
 * stripping
Class file compaction gain: 81.37746% (1734516 ==> 323011)
	  
	
      

Running the resulting application will print the amount of RAM memory that was required during the execution:

	>./caffeine_smart_analyse
		  
Sieve score = 364 (98)
Loop score = 328 (2017)
Logic score = 359 (0)
String score = 463 (708)
Float score = 215 (185)
Method score = 126 (166650)
Overall score = 285

### Application used at most 432197 bytes for the Java heap (accuracy 1%).
### Non-Java heap memory used: 68031 bytes.
###
###                     Worst case allocation overhead:
###     heapSize        dynamic GC      const GC work
###     1527k           6               3
###     1287k           7               4
...     ...             ...             ..
###     502k            256             69
###     498k            384             100
	  
	
      

Here, the application required 432197 byte for the Java heap that is under the control of the garbage collector, while addition 68031 bytes were required for global data such as stacks, buffers, etc. The total minimum heap size is 498k in this case.

Memory for Application's Data Structures

To optimize the memory required for point 1, the application's data structures, care is required by the application developer to allocate little memory and to make efficient use of it.

One important prerequisite to keep the applications RAM demand low is that the Java implementation introduces little memory overhead into every object. This becomes particularly important since Java applications typically allocate many small objects.

The per-object memory overhead in Jamaica is relatively small: Typically only 2 machine words are required for internal data such as the object's type information, a monitor for synchronisation and memory area information[1].

When smart linking is used. Jamaica automatically switches to a smaller object model with only 1 machine word per object for internal data. This is possible if no special memory areas are used by the application, no user class loaders are defined and the number of threads is limited. The use of the large object model can be enforced through option -large·

Memory for Internal Data of the VM

The amount of memory required for internal data structures using an application that was built using Jamaica is very low since only essential data that needs to be modified at runtime is stored in RAM. Most data that is constant will be read directly from the application data that can be stored in ROM.

Nevertheless, the amount of memory required for internal data depends on the size of the application including all library class files that may be required at run time. Library classes such as character encodings or network protocols are not needed by all applications so they do not neccessarily need to be included. The libraries that should be included can be set through the option -setLibaries. The following tables lists variables that can be set through this option:

Table 7-1. Libraries that can be specified via -setLibaries

NamePurposeDefault Setting
encodingssupported character encodings8859_1 8859_2 8859_3 8859_4 8859_5 UTF8
protocolssupported network protocols for URL connectionshttp file jar
calendar_localessupported localized calendarsde en nl
text_localessupported text locales af_ZA ar_AE ar_BH ar_DZ ar_EG ar_IN ar_IQ ar_JO ar_KW ar_LB ar_LY ar_MA ar_OM ar_QA ar_SD ar_SY ar_TN ar_YE be_BY bn_IN br_FR bs_BA ca_ES cs_CZ cy_GB da_DK de de_AT de_BE de_CH de_DE de_LU el_GR en en_AU en_BW en_CA en_DK en_GB en_HK en_IE en_IN en_NZ en_PH en_SG en_US en_ZA en_ZW es_AR es_BO es_CL es_CO es_CR es_DO es_EC es_ES es_GT es_HN es_MX es_NI es_PA es_PE es_PR es_PY es_SV es_US es_UY es_VE et_EE eu_ES fa_IR fi_FI fo_FO fr_BE fr_CA fr_CH fr_FR fr_LU ga_IE gd_GB gl_ES gv_GB he_IL hi_IN hr_HR hu_HU id_ID it_CH it_IT iw_IL ja_JP ka_GE kl_GL ko_KR kw_GB lt_LT lv_LV mi_NZ mk_MK mr_IN mt_MT nl nl_BE nl_NL nn_NO no_NO oc_FR pl_PL pt_BR pt_PT ro_RO ru_RU ru_UA se_NO sk_SK sl_SI sq_AL sr_YU sv_FI sv_SE ta_IN te_IN tg_TJ tl_PH tr_TR uk_UA ur_PK uz_UZ vi_VN yi_US zh_CN zh_HK zh_SG zh_TW

For our example appliation, it is sufficient to have the default encoding iso 8859_1 and no support for network protocols or locales is required. Consequently, we can set encodings to 8859_1 while all of protocols, calendar_locales and text_locales can be set to the empty set. The resulting call to build this the application looks as follows:

	>jamaica -smart 
-setLibraries="encodings=8859_1 protocols= text_locales= 
calendar_locales=" CaffeineMarkEmbeddedApp 
-destination caffeine_smart_enc_prot_text_cal 
		  
Jamaica Builder Tool 2.2 Release 1
Generating code for target 'linux-gnu-i686'
 + caffeine_smart_enc_prot_text_cal__.c
 * C compiling
 * linking
 * stripping
Class file compaction gain: 96.71511% (1334780 ==> 43846)
	  
	
	> filesize caffeine_smart_enc_prot_text_cal
	210944
	
      

More than 95% of the class file data could be removed such that the resulting application occupies only about 200kB. The resulting RAM demand is only 272k, the size of the Java heap could was reduced to 206127 bytes while Non-Java heap memory remained 68031 bytes.

Memory Required for Threads

To reduce the Non-Java heap memory, we need to reduce the stack sizes and the number of threads that will be created for the application. The following means exist to do this.

  1. Reducing Java Stack Size

    The size of the Java stack can be reduced via setting option -javaStackSize to a lower value than the default (typically 20k). To reduce the size to 4 kilobytes, -javaStackSize 4k can be used.

  2. Reducing C Stack Size

    On system that do not adjust the native stack size automatically (Linux is an example of a system with automatic stack size extension), the size of the C stack can be set accordingly via option -nativeStackSize.

  3. Disabling Finalizer Thread

    A Java application typically uses one thread that is dedicated to running the finlize() methods of objects that were found to be unreachable by the garbage collector. An application that does not allocate any such objects may not need this finalizer thread. The priority of the finalizer thread can be adjusted through the option -finalizerPri. Setting the priority to zero (-finalizerPri 0) deactivates the finalizer thread completely.

    Note that deactivating the finlizer thread may cause a memory leak since any objects that have a finlize() method can no more be reclaimed. Similarily, weak, soft and phantom references rely on the presence of a finalizer. If the resources available on the target system do not permit the use of a finalizer thread, the application may execute finlize() method explicitly be frequent calls to Runtime.runFinalization(). This wil also permit the use of weak, soft and phantom references even if no finalizer thread is present.

  4. Setting the Number of Threads

    The number of threads available for the application can be set using option -numThreads. The default setting for this option is two, which is enough for the finalizer thread and the main thread of the application.

    If the finalizer thread was deactivated and the application does not start any other threads, the number of threads can be reduced to one using -numThreads 1.

    Note that if profiling information was collected and is profided via -useProfile, it will be checked that the the number of threads provided to option -numThreads is at least the number or threads that were required during the profiling run. If this is not the case, a warning and the minimum number of threads during the profiling run will be displayed. This information can be used to adjust the number of threads to the minimum required by the application.

  5. Disabling Time Slicing

    On non-realtime systems that do not strictly respect thread priorities, Jamaica uses one additional thread to allow time slicing between threads. On realtime systems, this thread can be used to enforce round-robin scheduling of threads of equal priorities.

    On systems with tight memory demand, the thread required for timeslicing might be deactivated by setting the size of the time slice to zero via -timeSlice 0ns.

    In an application that uses threads of equal priorities, explicit calls to Thread.yield() are required to permit thread switches to another thread of the same priority if the time slicing thread was disabled.

    Note that the number of threads set by option -numThreads does not include the time slicing thread. Unlike when disabling the finalizer thread, which is a Java thread, when the time slicing thread is disabled, the argument to -numThreads should not be changed.

Applying this to our example application, we can reduce the Java stack to 4kB, deactivate the finalizer thread, set the number of threads to 1 and disable time slicing thread:

	>jamaica -smart 
-setLibraries="encodings=8859_1 protocols= text_locales= 
calendar_locales=" -javaStackSize 4k -finalizerPri 0 
-numThreads 1 -timeSlice 0ns CaffeineMarkEmbeddedApp 
-destination caffeine_smart_enc_prot_text_cal_js_fP_tS 
		  
Jamaica Builder Tool 2.2 Release 1
Generating code for target 'linux-gnu-i686'
 + caffeine_smart_enc_prot_text_cal_js_fP_tS__.c
 * C compiling
 * linking
 * stripping
Class file compaction gain: 96.71511% (1334780 ==> 43846)
	  
	
	> filesize caffeine_smart_enc_prot_text_cal_js_fP_tS
	210944
	
      

These options had no effect on the application size itself. However, the RAM demand of the application could be reduced significantly to only 138kB. Of this memory, the largest part, 129019 bytes, are required for the Java heap that is under the control of the garbage collector, the Non-Java heap memory is only 9645 bytes.

Notes

[1]

see the chapter on the Realtime Specification for Java for details on memory areas