What Is an Outofmemoryerror and How to Debug and Fix It

What is an OutOfMemoryError and how do I debug and fix it

An OutOfMemoryError is an exception thrown by the Java Virtual Machine (JVM) because it needs to allocate memory for a (new) object, but insufficient memory is available for the object. The JVM will have first tried to free memory used by dead objects, by running the garbage collector.

As an OutOfMemoryError is a VirtualMachineError, the JVM is permitted to throw it at any time, although it must try to release memory through garbage collection first.

However, in practice it is likely to be thrown from a new statement that tried to create an object for which memory could not be allocated. Therefore, you should first examine the stacktrace associated with the exception for clues about the cause of the problem, as you would for any other exception.

  • If the exception is thrown from an attempt to allocate an array (such as int[] values = new int[n]), the cause could be that you are trying to create an excessively large array (n is too large). Have you made a mistake in the calculation of the size of array you need?
  • If the exception is thrown from an attempt to allocate an array in a method of a container class written by someone else, the cause could be that your code is asking the container to store an excessive number of things. Methods such as ArrayList.reserve(int) and HashMap(int) must allocate storage for future use. Have you made a mistake in the calculation of the size of container you need?
  • If the exception is thrown from inside a loop, the cause could be that the code has looped too many times. Is your loop termination condition correct? If it is a for loop, are you asking it to loop the correct number of times?

If the stacktrace does not provide enough clues, you could try using a heap profiler. That is a monitoring program that enables you to examine the memory used for objects while the program runs, or examies a heap dump written when the program exits. It can provide information about the sizes, number and classes of objects stored in memory.

The JVM has a finite amount of memory made available to it. You might conclude that you program is behaving correctly, but just needs more memory to run than has been made available to it. If you do not explicitly tell the JVM how much memory to use, most implementations will choose a sensible default amount based on the amount of RAM that your computer has, but that amount could be too small for your program. Command line options for the JVM can control how much memory is made available. For most JVM implementations, the most important of those options are -Xmx and -Xms.

How to debug Java OutOfMemory exceptions?

Analyzing and fixing out-of-memory errors in Java is very simple.

In Java the objects that occupy memory are all linked to some other objects, forming a giant tree. The idea is to find the largest branches of the tree, which will usually point to a memory leak situation (in Java, you leak memory not when you forget to delete an object, but when you forget to forget the object, i.e. you keep a reference to it somewhere).

Step 1. Enable heap dumps at run time

Run your process with -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp

(It is safe to have these options always enabled. Adjust the path as needed, it must be writable by the java user)

Step 2. Reproduce the error

Let the application run until the OutOfMemoryError occurs.

The JVM will automatically write a file like java_pid12345.hprof.

Step 3. Fetch the dump

Copy java_pid12345.hprof to your PC (it will be at least as big as your maximum heap size, so can get quite big - gzip it if necessary).

Step 4. Open the dump file with IBM's Heap Analyzer or Eclipse's Memory Analyzer

The Heap Analyzer will present you with a tree of all objects that were alive at the time of the error.
Chances are it will point you directly at the problem when it opens.

IBM HeapAnalyzer

Note: give HeapAnalyzer enough memory, since it needs to load your entire dump!

java -Xmx10g -jar ha456.jar

Step 5. Identify areas of largest heap use

Browse through the tree of objects and identify objects that are kept around unnecessarily.

Note it can also happen that all of the objects are necessary, which would mean you need a larger heap. Size and tune the heap appropriately.

Step 6. Fix your code

Make sure to only keep objects around that you actually need. Remove items from collections in a timely manner. Make sure to not keep references to objects that are no longer needed, only then can they be garbage-collected.

How to identify the issue when Java OutOfMemoryError?

If you face it in production and you cannot really reason about it from stacktraces or logs, you need to analyze what was in there.

Get the VM to dump on OOM

-XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath="/tmp"

And use that for analysis. The memory analyzer tool (http://eclipse.org/mat/) is a good standalone program for this analysis.

OutOfMemoryError exception: Java heap space, how to debug...?

Short answer to explain why you have an OutOfMemoryError, for every centroid found in the file you loop over the already "registered" centroids to check if it is already known (to add a new one or to update the already registered one). But for every failed comparison you add a new copy of the new centroid. So for every new centroid it add it as many times as there are already centroids in the list then you encounter the first one you added, you update it and you leave the loop...

Here is some refactored code:

public class CentroidGenerator {

final Map<String, Centroid> centroids = new HashMap<String, Centroid>();

public Collection<Centroid> getCentroids() {
return centroids.values();
}

public void nextItem(FlickrDoc flickrDoc) {

final String event = flickrDoc.getEvent();
final Centroid existingCentroid = centroids.get(event);

if (existingCentroid != null) {
existingCentroid.update(flickrDoc);
} else {
final Centroid newCentroid = new Centroid(flickrDoc);
centroids.put(event, newCentroid);
}
}

public static void main(String[] args) throws IOException, SAXException {

// instantiate Digester and disable XML validation
[...]

// now that rules and actions are configured, start the parsing process
CentroidGenerator abp = (CentroidGenerator) digester.parse(new File("PjrE.data.xml"));

Writer writer = null;

try {
File fileOutput = new File("centroids.xml");
writer = new BufferedWriter(new FileWriter(fileOutput));
writeOuput(writer, abp.getCentroids());

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

}

private static void writeOuput(Writer writer, Collection<Centroid> centroids) throws IOException {

writer.append("<?xml version='1.0' encoding='utf-8'?>" + System.getProperty("line.separator"));
writer.append("<collection>").append(System.getProperty("line.separator"));

for (Centroid centroid : centroids) {
writer.append("<doc>" + System.getProperty("line.separator"));

writer.append("<title>" + System.getProperty("line.separator"));
writer.append(centroid.getTitle());
writer.append("</title>" + System.getProperty("line.separator"));

writer.append("<description>" + System.getProperty("line.separator"));
writer.append(centroid.getDescription());
writer.append("</description>" + System.getProperty("line.separator"));

writer.append("<time>" + System.getProperty("line.separator"));
writer.append(centroid.getTime());
writer.append("</time>" + System.getProperty("line.separator"));

writer.append("<tags>" + System.getProperty("line.separator"));
writer.append(centroid.getTags());
writer.append("</tags>" + System.getProperty("line.separator"));

writer.append("<geo>" + System.getProperty("line.separator"));

writer.append("<lat>" + System.getProperty("line.separator"));
writer.append(centroid.getLat());
writer.append("</lat>" + System.getProperty("line.separator"));

writer.append("<lng>" + System.getProperty("line.separator"));
writer.append(centroid.getLng());
writer.append("</lng>" + System.getProperty("line.separator"));

writer.append("</geo>" + System.getProperty("line.separator"));

writer.append("</doc>" + System.getProperty("line.separator"));

}
writer.append("</collection>" + System.getProperty("line.separator") + System.getProperty("line.separator"));

}

/**
* JavaBean class that holds properties of each Document entry. It is important that this class be public and
* static, in order for Digester to be able to instantiate it.
*/
public static class FlickrDoc {
private String id;
private String title;
private String description;
private String time;
private String tags;
private String latitude;
private String longitude;
private String event;

public void setId(String newId) {
id = newId;
}

public String getId() {
return id;
}

public void setTitle(String newTitle) {
title = newTitle;
}

public String getTitle() {
return title;
}

public void setDescription(String newDescription) {
description = newDescription;
}

public String getDescription() {
return description;
}

public void setTime(String newTime) {
time = newTime;
}

public String getTime() {
return time;
}

public void setTags(String newTags) {
tags = newTags;
}

public String getTags() {
return tags;
}

public void setLatitude(String newLatitude) {
latitude = newLatitude;
}

public String getLatitude() {
return latitude;
}

public void setLongitude(String newLongitude) {
longitude = newLongitude;
}

public String getLongitude() {
return longitude;
}

public void setEvent(String newEvent) {
event = newEvent;
}

public String getEvent() {
return event;
}
}

public static class Centroid {
private final String event;
private String title;
private String description;

private String tags;

private Integer time;
private int nbTimeValues = 0; // needed to calculate the average later

private Float latitude;
private int nbLatitudeValues = 0; // needed to calculate the average later
private Float longitude;
private int nbLongitudeValues = 0; // needed to calculate the average later

public Centroid(FlickrDoc flickrDoc) {
event = flickrDoc.event;
title = flickrDoc.title;
description = flickrDoc.description;
tags = flickrDoc.tags;
if (flickrDoc.time != null) {
time = Integer.valueOf(flickrDoc.time.trim());
nbTimeValues = 1; // time is the sum of one value
}
if (flickrDoc.latitude != null) {
latitude = Float.valueOf(flickrDoc.latitude.trim());
nbLatitudeValues = 1; // latitude is the sum of one value
}
if (flickrDoc.longitude != null) {
longitude = Float.valueOf(flickrDoc.longitude.trim());
nbLongitudeValues = 1; // longitude is the sum of one value
}
}

public void update(FlickrDoc newData) {
title = title + " " + newData.title;
description = description + " " + newData.description;
tags = tags + " " + newData.tags;
if (newData.time != null) {
nbTimeValues++;
if (time == null) {
time = 0;
}
time += Integer.valueOf(newData.time.trim());
}
if (newData.latitude != null) {
nbLatitudeValues++;
if (latitude == null) {
latitude = 0F;
}
latitude += Float.valueOf(newData.latitude.trim());
}
if (newData.longitude != null) {
nbLongitudeValues++;
if (longitude == null) {
longitude = 0F;
}
longitude += Float.valueOf(newData.longitude.trim());
}
}

public String getTitle() {
return title;
}

public String getDescription() {
return description;
}

public String getTime() {
if (nbTimeValues == 0) {
return null;
} else {
return Integer.toString(time / nbTimeValues);
}
}

public String getTags() {
return tags;
}

public String getLat() {
if (nbLatitudeValues == 0) {
return null;
} else {
return Float.toString(latitude / nbLatitudeValues);
}
}

public String getLng() {
if (nbLongitudeValues == 0) {
return null;
} else {
return Float.toString(longitude / nbLongitudeValues);
}
}

public String getEvent() {
return event;
}
}
}

How to deal with java.lang.OutOfMemoryError: Java heap space error?

Ultimately you always have a finite max of heap to use no matter what platform you are running on. In Windows 32 bit this is around 2GB (not specifically heap but total amount of memory per process). It just happens that Java chooses to make the default smaller (presumably so that the programmer can't create programs that have runaway memory allocation without running into this problem and having to examine exactly what they are doing).

So this given there are several approaches you could take to either determine what amount of memory you need or to reduce the amount of memory you are using. One common mistake with garbage collected languages such as Java or C# is to keep around references to objects that you no longer are using, or allocating many objects when you could reuse them instead. As long as objects have a reference to them they will continue to use heap space as the garbage collector will not delete them.

In this case you can use a Java memory profiler to determine what methods in your program are allocating large number of objects and then determine if there is a way to make sure they are no longer referenced, or to not allocate them in the first place. One option which I have used in the past is "JMP" http://www.khelekore.org/jmp/.

If you determine that you are allocating these objects for a reason and you need to keep around references (depending on what you are doing this might be the case), you will just need to increase the max heap size when you start the program. However, once you do the memory profiling and understand how your objects are getting allocated you should have a better idea about how much memory you need.

In general if you can't guarantee that your program will run in some finite amount of memory (perhaps depending on input size) you will always run into this problem. Only after exhausting all of this will you need to look into caching objects out to disk etc. At this point you should have a very good reason to say "I need Xgb of memory" for something and you can't work around it by improving your algorithms or memory allocation patterns. Generally this will only usually be the case for algorithms operating on large datasets (like a database or some scientific analysis program) and then techniques like caching and memory mapped IO become useful.

How to debug a java heap OutOfMemory error in a production environment?

Just take a heap dump of production server & analyse it with Eclipse Memory analysis tool. You can copy the heapdump to your local. Eclipse Memory Analyzer is the best tool for this job. However, trying to get the UI to run remotely is very painful. Launching Eclipse and updating the UI is an extra load on the JVM that is already busy analyzing a 30G heap dump. Fortunately, there is a script that comes with MAT to parse the the heap dump and generate HTML reports without ever having to launch Eclipse!

Check this out.

How to solve OutOfMemoryError: Java heap space

You have an infinite loop. Each iteration will add to the List until you run OutOfMemory. You need to change num during the loop in such a way that it becomes 1 at some point so the loop ends.

Out of memory error java.lang.OutOfMemoryError

So it's a user crash. You are out of luck then. So you have a user with a cheap device that can't handle that image. :)

The best I can suggest:

  • Create a downscaled version of the image
  • Do a try/catch in ActivityWorkout.onCreate
  • Capture the out-of-memory exception
  • Set a "this is a low-memory device" flag in the settings (use SharedPreferences)
  • Before loading the layout, read that flag
  • Depending on its value, conditionally use either the full, or the downscaled version of the image

Java error main java.lang.OutOfMemoryError: Java heap space

You have to increase the java heap space

In Run->Run Configuration find the Name of the class you have been running, select it, click the Arguments tab then add:

-Xms512M -Xmx1024M

to the VM Arguments section



Related Topics



Leave a reply



Submit