views:

3412

answers:

14

I have to serialize around a million items and I get the following exception when I run my code:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOfRange(Unknown Source)
    at java.lang.String.<init>(Unknown Source)
    at java.io.BufferedReader.readLine(Unknown Source)
    at java.io.BufferedReader.readLine(Unknown Source)
    at org.girs.TopicParser.dump(TopicParser.java:23)
    at org.girs.TopicParser.main(TopicParser.java:59)

How do I handle this?

+1  A: 

Start java with a larger value for option -Xmx, for instance -Xmx512m

krosenvold
+21  A: 

Ideally, restructure your code to use less memory. For example, perhaps you could stream the output instead of holding the whole thing in memory.

Alternatively, just give the JVM more memory with the -Xmx option.

Kieron
Just wanted to add that the default max heap size is 64MB (on 32bit systems). It's entirely likely you just need more than that. If you increase it (try doubling it) and still get an OOM, then look into making your code more efficient.
Mark Renouf
On a 32jvm I believe the maximum memory Sun's JVM can handle is around 1.8GB. I believe you can go higher on 64bit JVMs and JVMs from other vendors are known to push this limit even higher. Just a word of warning
basszero
+9  A: 

You should not handle it in code. OutOfMemory should not be caught and handled. Instead start your JVM with a bigger heapspace

java -Xmx512M

should do the trick.

See here for more details

Mo
A: 

You can increase the size of the memory java uses with the -Xmx-option, for instance:

java -Xmx512M -jar myapp.jar

Better is to reduce the memory-footprint of your app. You serialize millions of items? Do you need to keep all of them in memory? Or can you release some of them after using them? Try to reduce the used objects.

Mnementh
+6  A: 

You get an OutOfMemoryError because your program requires more memory than the JVM has available. There is nothing you can specifically do at runtime to help this.

As noted by krosenvold, your application may be making sensible demands for memory but it just so happens that the JVM is not being started with enough (e.g. your app will have a 280MB peak memory footprint but the JVM only starts with 256MB). In this case, increasing the size allocated will solve this.

If you feel that you are supplying adequate memory at start up, then it is possible that your application is either using too much memory transiently, or has a memory leak. In the situation you have posted, it sounds like you are holding references to all of the million items in memory at once, even though potentially you are dealing with them sequentially.

Check what your references are like for items that are "done" - you should deference these as soon as possible so that they can be garbage collected. If you're adding a million items to a collection and then iterating over that collection, for example, you'll need enough memory to store all of those object instances. See if you can instead take one object at a time, serialise it and then discard the reference.

If you're having trouble working this out, posting a pseudo-code snippet would help.

Andrzej Doyle
+2  A: 

Use the transient keyword to mark fields in the serialized classes which can be generated from existing data. Implement writeObject and readObject to help with reconstructing transient data.

Martin OConnor
+2  A: 

Hi,

In addition to some of the tips that have been give to you, as review the memory lacks and also start the JVM with more memory (-Xmx512M). Looks like you have a OutOfMemoryError cause your TopicParser is reading a line that probably is pretty big (and here is what you should avoid), you can use the FileReader (or, if the encoding is an issue, an InputStreamReader wrapping a FileInputStream). Use its read(char[]) method with a reasonably sized char[] array as a buffer.

Also finally to investigate a little why is the OutOfMemoryError you can use -XX:+HeapDumpOnOutOfMemoryError Flag in the JVM to get a dump heap information to disk.

Good luck!

David Santamaria
A: 

There's no real way of handling it nicely. Once it happens you are in the unknown territory. You can tell by the name - OutOfMemory**Error**. And it is described as:

Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector

Usually OutOfMemoryError indicates that there is something seriously wrong with the system/approach (and it's hard to point a particular operation that triggered it).

Quite often it has to do with ordinary running out of heapspace. Using the -verbosegc and mentioned earlier -XX:+HeapDumpOnOutOfMemoryError should help.

You can find a nice and concise summary of the problem at javaperformancetuning

Anonymous
+3  A: 

I know that the official Java answer is "Oh noes! Out of memories! I give in!". This is all rather frustrating for anyone who has programmed in environments where running out of memory is not allowed to be a fatal error (for example, writing an OS, or writing apps for non-protected OSes).

The willingness to surrender is necessary - you can't control every aspect of Java memory allocation, so you can't guarantee that your program will succeed in low-memory conditions. But that doesn't mean you must go down without a fight.

Before fighting, though, you could look for ways to avoid the need. Perhaps you can avoid Java serialization, and instead define your own data format which does not require significant memory allocation to create. Serialization allocates a lot of memory because it keeps a record of objects it has seen before, so that if they occur again it can reference them by number instead of outputting them again (which could lead to an infinite loop). But that's because it needs to be general-purpose: depending on your data structure, you might be able to define some text/binary/XML/whatever representation which can just be written to a stream with very little need to store extra state. Or you might be able to arrange that any extra state you need is stored in the objects all along, not created at serialization time.

If your application does one operation which uses a lot of memory, but mostly uses much less, and especially if that operation is user-initiated, and if you can't find a way to use less memory or make more memory available, then it might be worth catching OutOfMemory. You could recover by reporting to the user that the problem is too big, and inviting them to trim it down and try again. If they've just spend an hour setting up their problem, you do not want to just bail out of the program and lose everything - you want to give them a chance to do something about it. As long as the Error is caught way up the stack, the excess memory will be unreferenced by the time the Error is caught, giving the VM at least a chance to recover. Make sure you catch the error below your regular event-handling code (catching OutOfMemory in regular event handling can result in busy loops, because you try to display a dialog to the user, you're still out of memory, and you catch another Error). Catch it only around the operation which you've identified as the memory-hog, so that OutOfMemoryErrors you can't handle, that come from code other than the memory hog, are not caught.

Even in a non-interactive app, it might make sense to abandon the failed operation, but for the program itself to carry on running, processing further data. This is why web servers manage multiple processes such that if one page request fails for lack of memory, the server itself doesn't fall over. As I said at the top, single-process Java apps can't make any such guarantees, but they can at least be made a bit more robust than the default.

That said, your particular example (serialization) may not be a good candidate for this approach. In particular, the first thing the user might want to do on being told there's a problem is save their work: but if it's serialization which is failing, it may be impossible to save. That's not what you want, so you might have to do some experiments and/or calculations, and manually restrict how many million items your program permits (based on how much memory it is running with), before the point where it tries to serialize.

This is more robust than trying to catch the Error and continue, but unfortunately it's difficult to work out the exact bound, so you would probably have to err on the side of caution.

If the error is occurring during deserialization then you're on much firmer ground: failing to load a file should not be a fatal error in an application if you can possibly avoid it. Catching the Error is more likely to be appropriate.

Whatever you do to handle lack of resources (including letting the Error take down the app), if you care about the consequences then it's really important to test it thoroughly. The difficulty is that you never know exactly what point in your code the problem will occur, so there is usually a very large number of program states which need to be tested.

Steve Jessop
A: 

After you follow the suggestion of increasing heap space (via -Xmx) but sure to use either JConsole or JVisualVM to profile your applications memory usage. Make sure that memory usage does not continuously grow. If so you'll still get the OutOfMemoryException, it'll just take longer.

bcash
+6  A: 

Everyone else has already covered how to give Java more memory, but because "handle" could conceivably mean catch, I'm going to quote what Sun has to say about Errors:

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions.

(emphasis mine)

R. Bemrose
where should not doesn't mean must not! +1 I have a couple of cases where I do handle UnsatisfiedLinkError
Mario Ortegón
ThreadDeath is another one you might want to catch if you need to do some cleanup for said thread.
R. Bemrose
A: 

Interesting - you are getting an out of memory on a readline. At a guess, you are reading in a big file without linebreaks.

Instead of using readline to get the stuff out of the file as one single big long string, write stuff that understands the input a bit better, and handles it in chunks.

If you simply must have the whole file in a single big long string ... well, get better at coding. In general, trying to handle mutimegabyte data by stuffing it all into a single array of byte (or whatever) is a good way to lose.

Go have a look at CharacterSequence.

A: 

Before taking any dangerous, time-consuming or strategic actions, you should establish exactly what in your program is using up so much of the memory. You may think you know the answer, but until you have evidence in front of you, you don't. There's the possibility that memory is being used by something you weren't expecting.

Use a profiler. It doesn't matter which one, there are plenty of them. First find out how much memory is being used up by each object. Second, step though iterations of your serializer, compare memory snapshots and see what objects or data are created.

The answer will most likely be to stream the output rather than building it in memory. But get evidence first.

Marcus Downing
A: 

I have discovered an alternate, respecting all other views that we should not try to catch the memory out of exception, this is what I've learned in recent time.

catch (Throwable ex){
 if (!(ex instanceof ThreadDeath))
 {
  ex.printStackTrace(System.err);
 }}

for your reference: OutOfMemoryError any feedback is welcome.

Avishek Arang