The following code is causing a OutOfMemmoryError: heap space
for some 3 million rows.
Memory allocated to JVM is 4 GB, using 64 bit installation.
while (rs.next())
{
ArrayList<String> arrayList = new ArrayList<String>();
for (int i = 1; i <= columnCount; i++)
{
arrayList.add(rs.getString(i));
}
objOS.writeObject(arrayList);
}
The memory referenced by the ArrayList
is eligible for garbage collection in each iteration of the while loop, and internally JVM calls garbage collection (System.gc()
) before throwing an OutOfMemoryError
because of heap space.
So why is the exception occurring?
OutOfMemoryError: Java heap space. 1) An easy way to solve OutOfMemoryError in java is to increase the maximum heap size by using JVM options "-Xmx512M", this will immediately solve your OutOfMemoryError.
OutOfMemoryError is a runtime error in Java which occurs when the Java Virtual Machine (JVM) is unable to allocate an object due to insufficient space in the Java heap. The Java Garbage Collector (GC) cannot free up the space required for a new object, which causes a java. lang.
In some cases, the secondary intention of data serialization is to minimize the data's size which then reduces disk space or bandwidth requirements.
Once an object is not referenced by any other object, it can be cleared out of the heap, in order for the JVM to reclaim and reuse that space. The execution thread that is responsible to clear the heap space is the Garbage Collector.
Is objOS
an ObjectOutputStream
?
If so, then that's your problem: An ObjectOutputStream
keeps a strong reference to every object that was ever written to it in order to avoid writing the same object twice (it will simply write a reference saying "that object that I wrote before with id x").
This means that you're effectively leaking all ArrayList
istances.
You can reset that "cache" by calling reset()
on your ObjectOutputStream
. Since you don't seem to be making use of that cache between writeObject
calls anyway, you could call reset()
directly after the writeObject()
call.
I agree with @Joachim.
In addition, it is recommended (in good coding convention) that do not declare any object inside the loop. Instead, declare it just before the loop start and use the same reference for initialization purpose. This will ask your code to use the same reference for each iterations and cause less burden on memory release thread (i.e. Garbage collection).
The Truth
I have edited this because I feel that there may be many people who (like me before today) still believe that declaring an object inside loop could harm the memory management; which is wrong.
To demonstrate this, I have used the same code posted on stackOverflow for this.
Following is my code snippet
package navsoft.advskill.test;
import java.util.ArrayList;
public class MemoryTest {
/**
* @param args
*/
public static void main(String[] args) {
/* Total number of processors or cores available to the JVM */
System.out.println("Available processors (cores): "
+ Runtime.getRuntime().availableProcessors());
/*
* Total amount of free memory available to the JVM
*/
long freeMemory = Runtime.getRuntime().freeMemory();
System.out.println("Free memory (bytes): "
+ freeMemory);
/*
* This will return Long.MAX_VALUE if there is no preset limit
*/
long maxMemory = Runtime.getRuntime().maxMemory();
/*
* Maximum amount of memory the JVM will attempt to use
*/
System.out.println("Maximum memory (bytes): "
+ (maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory));
/*
* Total memory currently in use by the JVM
*/
System.out.println("Total memory (bytes): "
+ Runtime.getRuntime().totalMemory());
final int LIMIT_COUNTER = 1000000;
//System.out.println("Testing Only for print...");
System.out.println("Testing for Collection inside Loop...");
//System.out.println("Testing for Collection outside Loop...");
//ArrayList<String> arr;
for (int i = 0; i < LIMIT_COUNTER; ++i) {
//arr = new ArrayList<String>();
ArrayList<String> arr = new ArrayList<String>();
System.out.println("" + i + ". Occupied(OldFree - currentFree): "+ (freeMemory - Runtime.getRuntime().freeMemory()));
}
System.out.println("Occupied At the End: "+ (freeMemory - Runtime.getRuntime().freeMemory()));
System.out.println("End of Test");
}
}
The result from the output is clearly shows that there is no difference in occupying/freeing the memory if you either declare the object inside or outside the loop. So it is recommended to have the declaration to as small scope as it can.
I pay my thanks to all the experts on StackOverflow (specially @Miserable Variable) for guiding me on this.
Hope this would clear your doubts too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With