6

I'm writing a MIDlet using LWUIT and images seem to eat up incredible amounts of memory. All the images I use are PNGs and are packed inside the JAR file. I load them using the standard Image.createImage(URL) method. The application has a number of forms and each has a couple of labels an buttons, however I am fairly certain that only the active form is kept in memory (I know it isn't very trustworthy, but Runtime.freeMemory() seems to confirm this).

The application has worked well in 240x320 resolution, but moving it to 480x640 and using appropriately larger images for UI started causing out of memory errors to show up. What the application does, among other things, is download remote images. The application seems to work fine until it gets to this point. After downloading a couple of PNGs and returning to the main menu, the out of memory error is encountered. Naturally, I looked into the amount of memory the main menu uses and it was pretty shocking. It's just two labels with images and four buttons. Each button has three images used for style.setIcon, setPressedIcon and setRolloverIcon. Images range in size from 15 to 25KB but removing two of the three images used for every button (so 8 images in total), Runtime.freeMemory() showed a stunning 1MB decrease in memory usage.

The way I see it, I either have a whole lot of memory leaks (which I don't think I do, but memory leaks aren't exactly known to be easily tracked down), I am doing something terribly wrong with image handling or there's really no problem involved and I just need to scale down.

If anyone has any insight to offer, I would greatly appreciate it.

skaffman
  • 381,978
  • 94
  • 789
  • 754
  • 1
    not a solution but a tipp: optimize your png with optipng (http://optipng.sourceforge.net/) - cuts images with small number of colors often to 50% of its size without quality-reduction. – user181750 Feb 26 '10 at 14:03

5 Answers5

2

Mobile devices are usually very low on memory. So you have to use some tricks to conserve and use memory.

We had the same problem at a project of ours and we solved it like this.

for downloaded images: Make a cache where you put your images. If you need an image, check if it is in the cachemap, if it isn't download it and put it there, if it is, use it. if memory is full, remove the oldest image in the cachemap and try again.

for other resource images: keep them in memory only for as long as you can see them, if you can't see them, break the reference and the gc will do the cleanup for you.

Hope this helps.

Demian Kasier
  • 2,337
  • 6
  • 26
  • 39
1

There are a few things that might be happening here:

  • You might have seen the memory used before garbage collection, which doesn't correspond to the actual memory used by your app.
  • Some third party code you are running might be pooling some internal datastructures to minimize allocation. While pooling is a viable strategy, sometimes it does look like a leak. In that case, look if there is API to 'close' or 'dispose' the objects you don't need.
  • Finally, you might really have a leak. In this case you need to get more details on what's going on in the emulator VM (though keep in mind that it is not necessarily the same as the phone VM).

Make sure that your emulator uses JRE 1.6 as backing JVM. If you need it to use the runtime libraries from erlyer JDK, use -Xbootclasspath:<path-to-rt.jar>.

Then, after your application gets in the state you want to see, do %JAVA_HOME%\bin\jmap -dump:format=b,file=heap.bin <pid> (if you don't know the id of your process, use jps)

Now you've got a dump of the JVM heap. You can analyze it with jhat (comes with the JDK, a bit difficult to use) or some third party profilers (my preference is YourKit - it's commercial, but they have time-limited eval licenses)

ddimitrov
  • 3,139
  • 3
  • 28
  • 44
  • Analyzing the heap dump is definitely the way to the find leaks and inefficient memory use. Eclipse has one built-in. Window > Open Perspective > Memory Analisys. then open the dump file. – tom Mar 11 '10 at 11:35
1

I had a similar problem with LWUIT at Java DTV. Did you try flushing the images when you don't need them anymore (getAWTImage().flush())?

JoaoHornburg
  • 889
  • 1
  • 10
  • 22
1

Use EncodedImage and resource files when possible (resource files use EncodedImage by default. Read the javadoc for such. Other comments are also correct that you need to actually observe the amount of memory, even high RAM Android/iOS devices run out of memory pretty fast with multiple images.

Avoid scaling which effectively eliminates the EncodedImage.

Shai Almog
  • 49,879
  • 5
  • 30
  • 57
0

Did you think of the fact, that maybe loading the same image from JAR, many times, is causing many separate image objects (with identical contents) to be created instead of reusing one instance per-individual-image? This is my first guess.

KarolDepka
  • 7,188
  • 9
  • 37
  • 55
  • Each image I use more than once (and there is a fair number of those) is loaded only once as an Image object. Multiple Button and Label objects throughout the application are created using the same Image object, but that's something I can't work around. Since posting the question I did manage to work out one way to optimize - apparently HttpConnection eats up quite a bit of memory as well. Moving all HTTP communication to a single, separate thread and tidying the code up to make sure it dies and is garbage collected as soon as it's done helped a bit. –  Sep 09 '09 at 08:03