Memory Management in J2ME
2007-02-28
Scope of this article.
The intent of this article is to give a brief overview of how memory management works in Java and to touch on its effects with regards to developing J2ME MIDlets.
Most java programmers are aware of the System.gc() and/or Runtime.gc() methods offered by the Java API. However many programmers have only a partial grasp of the effects of calling either of these methods. Readers of this document will walk away with a more in-depth understanding of garbage collection and memory management in general.
Introduction to Garbage collection.
Garbage collection is a service that can be provided by the Java Virtual Machine to reclaim memory that is deemed eligible for reclamation. All editions of Java (J2ME, J2SE, and J2EE) are capable of providing this service. Unlike C++ there is no way to manually delete allocated memory. The application must leave memory management in the capable hands of the JVM. For this reason there is no concept of a pointer in Java. A pointer would allow access to memory arbitrarily and this would introduce possible issues for the garbage collector and other aspects of the JVM’s operation.
What happens when System.gc() is called?
There is no definitive answer to this question. The JVM is NOT even required to perform garbage collection services. That’s right. If you wanted to implement your own JVM you could make it a spec-compliant one without collecting any garbage at all, although it wouldn’t work for long periods.
It is very important to realize that calling this method is merely a suggestion to the JVM to perform garbage collection. Even if it supports garbage collection it may choose not to perform it. Also the JVM may decide to perform garbage collection on its own without the application calling System.gc() if it deems such an action necessary (such as dwindling free memory).
Assuming garbage collection does occur, how does it work?
Again, there is no definitive answer for this question. It may take a “mark and sweep” approach or other. This is up to the JVM vendor to decide. The BEA JRockit VM, for example, exposes various GC approaches to the administrator and he or she decides which to employ.
What about garbage collection IS definitive?
Only one thing is known for sure. In order for an object to be eligible for garbage collection, no live thread can have any way to access it. This can be accomplished in a few different ways:
How the JVM knows that an object has live references is, however, unknown. It might maintain counters or might have lookup tables or other. It is enough that the JVM knows which objects are and are not eligible for garbage collection.
Ensuring an object’s eligibility for garbage collection.
Following along the example above of what constitutes a live reference a good approach to avoid memory leaks is to use the singleton pattern for having a “reference manager”. Since only it maintains single references to all objects only one reference need be nullified.
Another approach that a reader pointed out (and was added above) was to kill the thread which is the sole maintainer of references to particular objects. Once the thread is killed the references die with it, making the objects automatically eligible for garbage collection without needing to nullify each one individually.
Preventing an object from being eligible for garbage collection.
While counter-productive to the concept of managing memory it is possible to temporarily thwart the garbage collector’s attempt to reclaim the memory allocated to an object. In the class’ finalize method you can assign an outside reference to the object before it is collected.
public void finalize() {
OutsideClass.referenceToMe = this;
}
This will only work the first time the garbage collector attempts to reclaim the object since the finalize method is only called once.
Memory management and its implications for J2ME MIDlets.
When developing applications targeting the J2SE platform memory optimization typically takes a backseat to more pressing development concerns. In the J2ME world this approach will quickly run a MIDlet into the ground. J2ME-enabled devices, in a bid to remain affordable, are slim on memory. Depending on the device there may be as little as a few hundred kilobytes of accessible heap space. For this reason solid memory managemement techniques are crucial to a MIDlet’s robustness. These techniques can also be applied, without any additional effort, to benefit J2SE and J2EE applications.
How do I deal with OutOfMemoryErrors?
Ideally you don’t or at least shouldn’t. Errors, as opposed to Exceptions are not meant to be caught by your application. The fact that this error occurs is a sign that you are trying to allocate more memory than is available as a single chunk. This usually occurs when you attempt to load a large image after loading many smaller ones. The smaller images used up half the heap and the large one can’t be provided a large enough heap chunk in which to fit. To overcome this, call System.gc() more often (even if its performance is not guaranteed) and avoid loading such large resources into memory. This way you shouldn’t need to catch the error.
Object reuse
In many cases an application instantiates an object, uses it, then discards it before instantiating another such object. This is not only inefficient with regards to time but can lead to accelerated memory fragmentation (unfortunately there isn’t a System.defragHeap() method) and is more prone to failure on devices with buggy garbage collection implementations.
To alleviate this issue lazy loading combined with the factory design pattern can be used. In this pattern an object factory is called upon to instantiate an object rather than manual instantiation via a constructor. The factory can, instead of always instantiating a new object, return an object which was previously marked as discarded (how this marking occurs may vary and is up to the developer). This approach will abstract the management of instances from the core MIDlet logic and will also enhance performance in the long run.
Conclusion
Call System.gc() but don’t forget it’s merely a suggestion that garbage collection take place and not a command to that effect.
Try to reuse objects so that you can lessen your reliance on the already highly non-deterministic garbage collector.
When possible load large resources ahead of smaller ones to minimize the potential effects of memory fragmentation.
Entry Filed under: programming (advanced). .
Trackback this post | Subscribe to the comments via RSS Feed