Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoid memory fragmentation when allocating lots of arrays in Java

I am developing an application in Java that runs on Windows Mobile devices. In order to achieve this we have been using the Esmertec JBed JVM, which is not perfect but we are stuck with it for now. Recently we have been getting complaints from customers about OutOfMemoryErrors. After a lot of playing around with things I discovered that the device has plenty of free memory (approx. 4MB).

The OutOfMemoryErrors always occur at the same point in the code and that is when expanding a StringBuffer in order to append some characters to it. After adding some logging around this area I found that my StringBuffer had about 290000 characters in it with a capacity of about 290500. The expansion strategy of the internal character array is simply to double the size, so it would be attempting to allocate an array of about 580000 characters. I printed out the memory usage around this time too and found that it was using about 3.8MB of about 6.8MB total (although I have seen the total available memory rise to around 12MB at times, so there is plenty of room for expansion). So it is at this point that the application reports an OutOfMemoryError, which doesn't make much sense given how much there is still free.

I started thinking about the operation of the application up to this point. Basically what is happening is I am parsing an XML file using MinML (a small XML Sax Parser). One of the fields in the XML has about 300k characters in it. The parser streams the data from disk and by default it loads only 256 characters at a time. So when it reaches the field in question the parser will call the 'characters()' method of the handler over 1000 times. Each time it will create a new char[] holding 256 characters. The handler simply appends these characters to a StringBuffer. The default initial size of the StringBuffer is only 12, so as the characters are appended to the buffer it is going to have to grow a number of times (each time creating a new char[]).

My assumption from this was that it is possible that while there is enough free memory since the previous char[]s can be garbage collected, maybe there is no contiguous memory block big enough to fit the new array I am trying to allocate. And maybe the JVM is not smart enough to expand the heap size because it is stupid and thinks there is no need because apparently there is enough free memory.

So my question is: does anyone have any experience of this JVM and might be able to conclusively confirm or disprove my assumptions about memory allocation? And also, does anyone have any ideas (assuming my assumptions are correct) about how to imrove the allocation of the arrays so that the memory won't become fragmented?

Note: things I've tried already:

  • I increased the initial array size of the StringBuffer and I increaed the read size of the parser so that it wouldn't need to create so many arrays.
  • I changed the expansion strategy of the StringBuffer so that once it reached a certain size threshold it would only expand by 25% rather than 100%.

Doing both of these things helped a little, but as I increase the size of the xml data going in I still get OutOfMemoryErrors at a fairly low size (approx. 350kb).

Another thing to add: all of this testing was performed on a device using the JVM in question. If I run the same code on the desktop using the Java SE 1.2 JVM I don't have any problems, or at least I don't get the problem until my data reaches about 4MB in size.

EDIT:

another thing I have just tried which has helped a bit is I set the Xms to 10M. So this gets past the problem of the JVM not expanding the heap when it should and allows me to process more data before the error occurs.

like image 938
DaveJohnston Avatar asked Jan 14 '10 19:01

DaveJohnston


People also ask

How do you avoid memory fragmentation?

Reduce the number of allocations and deallocations If you can reduce the number of memory allocations and memory deallocations you are reducing the chance for fragmentation to occur. As such anything you can do to reduce how often you allocate or deallocate memory will usually help.

What causes memory fragmentation?

Memory paging creates internal fragmentation because an entire page frame will be allocated whether or not that much storage is needed. Due to the rules governing memory allocation, more computer memory is sometimes allocated than is needed.

What is memory fragmentation in Java?

Memory fragmentation is when your memory is allocated in a large number of non-sequential blocks with gaps that can't be used for new allocations due to size differences.

What is heap fragmentation?

Heap fragmentation is a state in which available memory is broken into small, noncontiguous blocks. When a heap is fragmented, memory allocation can fail even when the total available memory in the heap is enough to satisfy a request, because no single block of memory is large enough.


2 Answers

Maybe you could try VTD light. It seems more memory efficient than SAX. (I know it's a huge change.)

like image 149
superfav Avatar answered Sep 28 '22 10:09

superfav


Just for the sake of updating my own question I have found that the best solution was to set the minimum heap size (I set it to 10M). This means that the JVM never has to decide whether or not to expand the heap and therefore it never (so far in test) dies with an OutOfMemoryError even though it should have plenty of space. So far in test we have been able to triple the amount of data we parse without an error and we could probably go further if we actually needed to.

This is a bit of a hack for a quick solution to keep existing customers happy, but we are now looking at a different JVM and I'll report back with an update if that JVM handles this scneario any better.

like image 29
DaveJohnston Avatar answered Sep 28 '22 11:09

DaveJohnston