Garbage Collection

One of the great benefits of Java is garbage collection. The garbage collector frees (or reclaims) memory as objects are no longer in use. For example, the Record object allocated in doSomethingWithAllRecords() in Listing 4–15 is made eligible for garbage collection when the method returns, as a reference to that object no longer exists. There are two very important things to note:

  • Memory leaks can still exist.
  • Use the garbage collector to help you manage memory as it does more than just freeing memory not in used anymore.

Memory Leaks

As memory can be reclaimed only when an object is no longer referenced, a memory leak can occur when a reference to an object is kept around. A typical example from the Android documentation is when the whole Activity object is leaked as the screen turns (for example, from portrait to landscape orientation). This particular example is easy to reproduce and is quite serious as an Activity object uses quite a bit of memory (and often contains references to even more objects). There is no easy solution to avoiding memory leaks, however Android provides you with tools and APIs to help you.

The DDMS perspective in Eclipse lets you track memory usage and memory allocation with the Heap and Allocation Tracker, respectively. Once again, these tools are not going to tell you where the memory leak is (if there is any), but you can use them to analyze your application's memory usage and hopefully find out if your application has leaks.

TIP: Use the Eclipse Memory Analyzer to even better analyze your memory usage. You can download it from http://www.eclipse.org/mat.

Android 2.3 defines the StrictMode class, which can be of great help to detect potential memory leaks. While StrictMode's virtual machine policy in Android 2.3 lets you detect only when SQLite objects (such as cursors) are not closed, StrictMode's VM policy in Android 3.0 and above also lets you detect these potential leaks:

  • Activity leaks
  • Leaks of other objects
  • Leaks when objects are not closed (see Android documentation for complete list of classes implementing the Closeable interface)

NOTE: The StrictMode class was introduced in Android 2.3 (API level 9), but additional functionalities were added in Android 3.0 (API level 11) in both the VM policy and thread policy. For example, Honeycomb's StrictMode's thread policy supports flashing the screen when a violation is detected.

Listing 4–17 shows how to use the StrictMode class to detect memory leaks in your application. You should enable this feature only during development and testing, and disable it as you release your application into the wild.

Listing 4–17. Using StrictMode

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        builder.detectLeakedSqlLiteObjects();
        if (VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            builder.detectActivityLeaks().detectLeakedClosableObjects();
        }
        // or you could simply call builder.detectAll()

        // penalty
        builder.penaltyLog(); // other penalties exist (e.g. penaltyDeath()) and can be combined

        StrictMode.VmPolicy vmp = builder.build();
         StrictMode.setVmPolicy(vmp);
    }
}

In that particular instance, StrictMode detects a violation when a closeable object (SQLite object or other) is not closed, and will only log the violation. To verify the behavior, you can simply query a database and purposely forget to close the returned cursor, for example by modifying the code shown in Listing 1-25 in Chapter 1.

As the StrictMode class evolves, it is recommended you simply use detectAll(), which allows you to test your application with future Android releases while taking advantage of the new functionalities the StrictMode class supports.

References

While freeing memory is an important feature of the garbage collector, it does more than that as it is a complete memory management system. Everybody writing Java code has heard about references and how an object can be referenced or not. However, too few seem to know about the multiple types of references. In fact, Java defines four types of references:

  • Strong
  • Soft
  • Weak
  • Phantom
Strong References

Strong referencesare the references Java developers are the most familiar with. Creating such a reference is trivial and is done all the time in Java, as shown in Listing 4–18. In fact, they are the references your application should use most of the time. Two strong references are created, one to an Integer object and one to a BigInteger object.

Listing 4–18. Strong References

    public void printTwoStrings (int n) {
        BigInteger bi = BigInteger.valueOf(n); // strong reference
        Integer i = new Integer(n); // strong reference

        System.out.println(i.toString());
        i = null; // Integer object freshly created is now eligible for garbage collection
    
        System.out.println(bi.toString());
        bi = null; // BigInteger object may not be eligible for garbage collection here!
    }

The important thing to notice here is that while setting i to null does make the Integer object eligible for garbage collection, setting bi to null may not. Because the BigInteger.valueOf() method may return a preallocated object (for example, BigInteger.ZERO), setting bi to null merely removes one strong reference to the BigInteger object, but more strong references to that same object may still exist. Two more strong references are created in that method, and they may not be as obvious as the other ones: the calls to i.toString() and bi.toString() each create a strong reference to a String object.

NOTE: Strictly speaking, you would have to know the implementation of the Integer constructor to make sure no strong reference to the new Integer object is created anywhere else and therefore make sure that setting i to null does indeed make the object eligible for garbage collection.

As discussed earlier, keeping strong references to objects around can cause memory leaks. We've probably used the term “strong reference” too many times, so it is time to say that Java does not really define such a term or class. Strong references are “normal” references, simply referred to (pun intended) as references.

Soft, Weak, and Phantom References

Soft and weak references are similar in nature, as they are references that are not strong enough to keep an object from being deleted (or reclaimed). They differ in how aggressively the garbage collector will try to reclaim the object they have a reference to.

An object that is softly reachable, that is, for which there exists a soft reference but no strong reference, is likely to be left alone by the garbage collector when the collection occurs but there is still enough memory to hold the object. However, if the garbage collector determines it needs to reclaim more memory, then it is free to reclaim the softly reachable object's memory. This type of reference is the perfect candidate for a cache that can automatically remove its entries.

TIP: When using a cache, make sure you understand what type of reference it uses. For example, Android's LruCache uses strong references.

Weakly reachable objects, that is, objects for which there exists a weak reference but no strong or soft reference, may be reclaimed as soon as the next collection happens. In other words, the garbage collector will more aggressively reclaim the memory of weakly reachable objects. This type of reference is the perfect candidate for mappings that can be removed automatically as keys are no longer referenced. Use the WeakHashMap class for this purpose.

NOTE: How aggressive the garbage collector is depends on the actual implementation.

Phantom references are the weakest of the references and are rarely used. They can be useful if your application needs to know when an object is being garbage collected and you need to perform some clean-up at that time. To be truly useful, phantom references should be registered with a reference queue.

Soft, weak, and phantom references are actually objects themselves and offer a level of indirection to any other object. For example, you could create a phantom reference to a soft reference to a weak reference. In practice though, you will almost always create soft, weak, or phantom references to “strong” references. Listing 4–19 shows an example of soft and weak references being created, each associated with a different reference queue.

Listing 4–19. References and Reference Queues

    private Integer strongRef;
    private SoftReference<Integer> softRef;
    private WeakReference<Integer> weakRef;
    private ReferenceQueue<Integer> softRefQueue = new ReferenceQueue<Integer>();
    private ReferenceQueue<Integer> weakRefQueue = new ReferenceQueue<Integer>();

    public void reset () {
        strongRef = new Integer(1);
        softRef = new SoftReference<Integer>(strongRef, softRefQueue);
        weakRef = new WeakReference<Integer>(strongRef, weakRefQueue);
    }

    public void clearStrong () {
        strongRef = null; // no more strong reference, but weak and soft references may still exist
    }

    public void clearSoft () {
        softRef = null; // no more soft reference, but strong and weak references may still exist
    }

    public void clearWeak () {
        weakRef = null; // no more weak reference, but strong and soft references may still exist
    }

    public void pollAndPrint () {
        Reference<? extends Integer> r;
        if ((r = softRefQueue.poll()) != null) {
            do {
                Log.i(TAG, "Soft reference: " + r);
            } while ((r = softRefQueue.poll()) != null);
        } else {
            Log.i(TAG, "Soft reference queue empty");
        }
        if ((r = weakRefQueue.poll()) != null) {
            do {
                Log.i(TAG, "Weak reference: " + r);
            } while ((r = weakRefQueue.poll()) != null);
        } else {
            Log.i(TAG, "Weak reference queue empty");
        }
    }

    public void gc() {
    System.gc();
    }

Experiment with this code to see when references are enqueued and how this can affect your application. To take full advantage of the garbage collector's memory management abilities, it is important to understand references. You should not try to implement a similar memory management system when working with caches or maps. Most of the things you would want to achieve may be left to the garbage collector with a careful use of references.

Garbage Collection

Garbage collection can occur at various times, and you have little control over when it is happening. You may be able to give some hint to Android by calling System.gc(), but ultimately the Dalvik virtual machine gets to decide when garbage collection actually occurs. There are five situations that prompt garbage collection to occur, and I'll refer to them by their log messages, which you can see when using logcat.

  • GC_FOR_MALLOC: Occurs when the heap is too full to allocate memory, and memory must be reclaimed before the allocation can proceed
  • GC_CONCURRENT:  Occurs when a (possibly partial) collection kicks in, usually as there are enough objects to reclaim
  • GC_EXPLICIT: Can occur when you call System.gc() to explicitly request a garbage collection
  • GC_EXTERNAL_ALLOC: Does not occur anymore on Honeycomb or later (as everything is allocated in the heap)
  • GC_HPROF_DUMP_HEAP: Occurs when you create an HPROF file

Listing 4–20 shows some log messages from the garbage collector.

Listing 4–20. Garbage Collection Messages

GC_CONCURRENT freed 103K, 69% free 320K/1024K, external 0K/0K, paused 1ms+1ms
GC_EXPLICIT freed 2K, 55% free 2532K/5511K, external 1625K/2137K, paused 55ms

As garbage collection takes time, reducing the number of objects you allocate and then release can improve performance. This is especially true in Android 2.2 and earlier versions because garbage collection occurred in the application's main thread and could cause pretty serious issues with responsiveness and performance. For example, some frames in a real-time game could be dropped because too much time was spent doing garbage collection. Things got better with Android 2.3, with most of the garbage collection work relocated to a separate thread. As garbage collection occurs, the main thread is still affected a little bit (a pause of 5 milliseconds or less), but much less than in previous versions of Android. It is not uncommon for a complete garbage collection to take over 50 milliseconds. For reference, a 30 frames-per-second game would need about 33 milliseconds to render and display each frame, so it is easy to see why garbage collection on pre-Android 2.3 systems could cause problems.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.167.161