The Source for Java Technology Collaboration


BD-J Images and Memory Management

Introduction

In an embedded device like a Blu-ray player, careful memory management by applications is essential for reliability. In a typical BD-J xlet, images are often the largest items in memory. The java.awt APIs for loading and disposing of images are a little bit quirky, so their correct use isn't always obvious. The need to explicitly manage image memory isn't always apparent to Java developers, particularly those who come from a desktop background.

Loading images and other issues are discussed in the HD Cookbook starting on page 17-9, in the sections "Working with Images" and "Combining Small Images." The more advanced subject of freeing image memory is not discussed in the cookbook. In this wiki entry, we explore best practices and APIs for managing image memory in an effective manner.

The Problem

Generally speaking, in Java the memory associated with objects is freed automatically, by the garbage collector. The same is true for image objects, but on an embedded device effectively freeing image memory presents special difficulties for the platform implementor. Images are usually stored in native memory (e.g. using malloc()). In a good quality implementation, garbage collection will be triggered when image memory begins running out, even if it stored outside the Java heap, e.g. in malloc space. This can be difficult to achieve, so in practice implementation quality varies. On some players, it's possible you might see failure due to lack of image buffer memory, even if that memory could be made available by GCing unreferenced images and reclaiming/compacting image buffer memory.

The Solution

An application can help the platform greatly by explicitly destroying images, thus freeing the native resources associated with those images. The API for doing this is a little bit confusing, partly because such explicit deallocation of images isn't necessary on a desktop platform with generous amounts of memory. Explicit deallocation of images wasn't supposed to be necessary on embedded devices, but pragmatically speaking, application authors really should do everything they can to help the platform.

Normal Images

The class java.awt.Image has a method called flush() defined. For a normal image, like a PNG image loaded from a disc, this method frees the pixmap data, so it works great for cleaning up native memory. As an application author, you might need to do reference-counting to ensure that you always flush images that are no longer used, and you never flush images that are stil used. You should also be sure to create images with the createImage() family of methods defined on java.awt.Toolkit, and not the getImage() methods. That's because getImage() tries to share image instances. getImage() can even result in two xlets sharing the same instance of an image, so that flushing an image in one xlet might corrupt the state of another xlet!

In the HD cookbook library, the class ManagedImage in com.hdcookbook.grin.util handles reference counting, and flushing images when not in use.

Images Buffers

Image buffers present a special problem. In the original design of Image, flush() was specified to flush only cached information that can be reconstructed. With a normal image, you can throw away the pixmap, and if you need the image again, you can always decode the .png file again (assuming, of course, that the data source of the .png file is available, but the image API makes this assumption). With an image buffer like java.awt.image.BufferedImage, however, the pixmap is the only copy of the image data. For that reason, flush() on a BufferedImage is effectively a NOP - it does not free the large native pixmap buffer.

In java.awt from Personal Basis Profile, garbage collection will of course eventually free a BufferedImage, but there is no API that immediately frees the pixmap buffer. This is unfortunate. This is solved for BD-J and other GEM platforms with a different buffered image class called DVBBufferedImage in the package org.dvb.ui. DVBBufferedImage defines a new dispose() method that frees the pixmap. Note that DVBBufferedImage also has the flush() method inherited from java.awt.Image, but this method does not flush the pixmap. That's because the contract of flush() only allows flushing cached data that can be reconstructed.

In the HD Cookbook menu application, we use DVBBufferedImage in this way. See AssetFinder in com.hdcookbook.util and MenuAssetFinder in com.hdcookbook.bookmenu.menu.MenuAssetFinder to see how this is done. It's a little more complicated than just using DVBBufferedImage because we want our code to also work on desktop java, which doesn't include this class. For this reason we go through the AssetFinder to delegate image buffer operations to a place that can be overridden in the BD-J xlet. The default DVBBufferedImage image type is functionally equivalent to what you get from the AWT createCompatibleImage() method defined on java.awt.GraphicsConfiguration.

Note that DVBBufferedImage was originally written for pJava (which didn't have Graphics2D), but it was written to coexist harmoniously with PBP (which does). It is guaranteed that the results of calling DVBBufferedImage.createGraphics() is an instanceof Graphics2D. This means that you can directly cast this value to the type java.awt.Graphics2D. This is specified in MHP, which requires that all instances of DVBGraphics be instances of Graphics2D. The specification for this is in the class description of DVBGraphics. To cast the Graphics object, the following should work:

    import java.awt.graphics2D;
    import org.dvb.DVBBufferedImage;
    ...
        DVBBufferedImage buffer;
        buffer = new DVBBufferedImage(1920, 1080);
        Graphics2D g = (Graphics2D) buffer.createGraphics();
However, when you compile this code, you may find that the compiler fails with an error message about "inconvertible types." If this happens, it's probably because the compilation stubs you're using for the org.dvb classes is based on the old pJava version of GEM. If you see this, please report this as a bug to your stubs provider, but don't worry, there's a workaround: just cast through Object, as follows:
        DVBBufferedImage buffer;
        buffer = new DVBBufferedImage(1920, 1080);
        Object o = buffer.createGraphics();
        Graphics2D g = (Graphics2D) o;
This workaround doesn't incur any significant runtime penalty.

Summary

When writing an application, be sure that you

  • Call flush() on normal images when you're done with them
  • Use DVBBufferedImage instead of AWT's BufferedImage, so you can call dispose() on it when you're done

By doing this, you help the player free image memory as soon as possible. Since the image pixmap memory is typically native, this helps the player and the application avoid running out of memory.

-- Main.billf - 25 Apr 2008
-- Main.billf - 26 Jun 2008

Topic BDJImageMemoryManagement . { Edit | Ref-By | Printable | Diffs r9 < r8 < r7 < r6 < r5 | More }
 XML java.net RSS

Revision r9 - 07 Jan 2009 - 00:48:35 - Main.billf
Parents: WebHome > Blu-RayDisc
Mobileandembedded.BDJImageMemoryManagement moved from Mobileandembedded.ImageMemoryManagement on 25 Apr 2008 - 10:09 by Main.billf - put it back