Mike Kirkup, Research In Motion

The Low Memory Manager (LMM) is used to maintain memory Resources on the BlackBerry handheld when those Resources pass a low-level threshold indicating that memory Resources are in scarce supply. The LMM will attempt to free up existing memory in an effort to provide more memory space on the handheld. All applications, including third party solutions, should work with the LMM to clear up as much space as possible when the handheld is running low on memory Resources.

Low Memory Manager Conditions

It is important to understand that there are three conditions that can cause the LMM to try to free up memory Resources.

The amount of available flash memory on the handheld decreases below a certain threshold. The free flash threshold is actually dependent on the amount of free RAM in the system. Generally, the free flash threshold varies between 400 KB and 800 KB with a guarantee to invoke the LMM if free flash drops below 400KB.

The number of object handles available on the handheld decreases below 1,000 handles. The number of available handles depends on the amount of flash on the handheld. For 8MB handhelds, there are approximately 12,000 available handles. For 16 MB handhelds, there are approximately 27,000 available handles.

The number of reference ordinals available on the handheld decreases below a certain threshold. On current handhelds this threshold is set to 1,000 reference ordinals. The number of available reference ordinals depends on the amount of flash on the handheld. For 8MB handhelds, there are approximately 24,000 available reference ordinals. For 16MB handhelds, there are approximately 56,000 available reference ordinals.

Working with the Low Memory Manager

There are two stages for applications to take advantage of the LMM . We will discuss these two stages in detail.

  1. Registering your application as a LowMemoryListener.
  2. Handling events received by the LowMemoryListener.

Stage # 1 - Registering your application as a LowMemoryListener

In order to use the LMM , you need to register your application with the LMM . The only way to do that is to provide an implementation of the LowMemoryListener interface in your application. When your application is started for the first time, you want to register the listener implementation with the LMM . Note that you only want to register your listener once. It is very important not to register more than once because you would receive multiple calls when the LMM is invoked.

The LowMemoryListener has the following method:

public boolean freeStaleObject( int priority )

This method will be automatically invoked when the LMM recognizes that the system is running low on memory, or when it has been invoked directly by a call to the poll method in the LowMemoryManager class.

In order to implement the freeStaleObject() method, you need to first understand the concept of where priority comes into this discussion. There are three levels of priority: low, medium and high. In each case, the operation of the application could differ in terms of what memory Resources it gives up.

For example, when freeStaleObject() is invoked with low priority, the application should consider clearing up some transitory variables and anything that is currently not necessary for complete functionality such as cached data. No extraordinary efforts should be made.

When invoked with medium priority, the application should consider cleaning up stale data such as very old emails or old calendar entries.

When invoked with high priority, the application should clean out objects in the application on a Least Recently Used (LRU) policy. For example, when the LMM is invoked on the Email application it will start to delete messages at the bottom of the list of emails since those are likely to be the least used. The application should remove all the stale objects it has in order to reduce the amount of memory consumed on the handheld.

The freeStaleObject() method returns a boolean which is used to indicate if persistent data was released during the call to freeStaleObject() . It returns true if persistent data was released.

Implementation Details

Now that we have discussed the different approaches to freeing objects and how that relates to the priority, it is important to discuss how an application should go about freeing up objects. The LMM provides one method that enables this exact procedure.

The application should first remove all references to an object and delete it from its data structures. On completion, the application should call LowMemoryManager.markAsRecoverable() , passing in the object that should be freed. This command will indicate to the underlying JVM that this object can be removed as part of the LMM operation.

It is important to call the markAsRecoverable() method because the LMM has a specific amount of memory it is trying to recover. It will spread this amount over all of the registered applications. If your application does free up memory Resources it is important to notify the LMM of this fact so that it can count that freed memory against its target amount.

The following is a sample implementation of the freeStaleObject() method that frees up items from different persisted vectors associated with each of the priority levels.

/**
* The implementation of the freeStaleObject method required by the
* LowMemoryListener. This method is invoked when the LMM is running out of
* memory related Resources. The application is asked to free up Resources according
* to the priority level passed into this method.
* @param priority the priority of the call which is either High, Medium or Low.
* @return a boolean indicating whether or not memory was freed by this call.
* It is VERY important that the proper value is returned from this method.
*/
public boolean freeStaleObject( int priority )
{
boolean dataFreed = false;

switch( priority ) {
case LowMemoryListener.HIGH_PRIORITY:
dataFreed = freeVector( _data._high );
_priority = LowMemoryListener.LOW_PRIORITY;
break;
case LowMemoryListener.MEDIUM_PRIORITY:
dataFreed = freeVector( _data._medium );
_priority = LowMemoryListener.HIGH_PRIORITY;
break;
case LowMemoryListener.LOW_PRIORITY:
dataFreed = freeVector( _data._low );
_priority = LowMemoryListener.MEDIUM_PRIORITY;
break;
}

if( dataFreed ) {
_persist.commit();
}
return dataFreed;
}

/**
* A private method that will free up the priority vector.
* @param vector the vector to free up.
* @return a boolean indicating whether any objects were freed by this method.
*/
private boolean freeVector( Vector vector )
{
boolean dataFreed = false;
int size = vector.size();

for( int i = size - 1; i >= 0; i-- ) {
Object obj = vector.elementAt( i );
vector.removeElementAt( i );
LowMemoryManager.markAsRecoverable( obj );
dataFreed = true;
}

return dataFreed;
}