Reputation: 869
I am doing a little experiment with Java and its capacity to allow memory leaks. I wrote out this simple code:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
class Obj {
int i;
Obj(int i) {
this.i = i;
}
}
List<Obj> list;
while(true) {
list = new ArrayList<Obj>();
for(int i = 0; i < 1000; i++) {
Obj o = new Obj(i);
list.add(o);
}
try {
Thread.sleep(1); //<-- added to give the gc time to trash the previous iteration
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
When this is executed (especially with more objects being added to the list every iteration), you can watch the amount of memory used increase quickly. I was able to hit 2 gigabytes very quickly with 10000 objects being added at every iteration. It would appear as though lists (and other types of objects that utilizes a "linked list" list like function) do not like being deleted.
When I did not add the objects to the list, the memory heap did not increase at all (meaning the garbage disposal was doing its job). I tried resetting the list to null every iteration then re-declaring it as well as calling the clear() method. Nothing seems to work. Whenever I use a List in a loop, my RAM cries for help.
So, why does this occur? Why doesn't the garbage disposal get rid of the list every iteration instead of letting them stack up? Does the List interface not allow this sort of usage? Am I just not giving the garbage disposal enough time to get rid of the last instance?
Upvotes: 1
Views: 1902
Reputation: 500457
Your code is fine. Unless you've hit some obscure -- and rare -- bug in the JVM, there is no memory leak either. What is likely happening is that you've given the JVM a lot of memory to play with, and it's decided to make use of that memory.
Having experimented with your code, the only way I can reproduce what you're seeing is by configuring a very large young generation heap (-Xmn
). The JVM has no need to run the garbage collector until the young generation is full, so the process ends up using quite a lot of memory.
However, when the young generation heap gets full, the GC does get run and does collect all the unreachable objects.
On the following screenshot, the top-right graph is showing the heap size:
As you can see, there is a drastic drop just before 19:47:50. This is when the young generation got full, and when the GC got run and has collected all the old lists.
Note that if you're using OS tools to monitor memory usage, you would likely not see a drop when the garbage collector is run. When some heap objects are freed, the memory they used to occupy doesn't usually get released back to the OS. It can, however, be reused by the same process.
If memory usage is an issue, you need to revisit the options you're supplying to the JVM.
Upvotes: 5
Reputation: 4004
There's no memory leak in your code. You can be 100% sure about that. However, this doesn't mean you cannot run out of memory even if all your currently active objects are smaller than your heap.
You cannot test garbage collection with a code like yours. GC is not invoked every time and doesn't clean everything at once. If you have big chunks of data refreshed at a fast rate, your GC won't have time to kick in and you may be out of memory with your code.
It's also important to understand that memory leaks are tested with profiling tools and not by monitoring whether application runs out of memory.
Upvotes: 0
Reputation: 11
(Normally I'd just comment and ask for your set VM options, but I don't have enough rep points to do that)
I'm just guessing, but perhaps you set -Xms to 1GB? This code doesn't leak, but depending on your VM options the process might still take a lot of memory.
Upvotes: 0
Reputation: 17361
You'll need to set list
to null
after every iteration in the while
loop. This way the GarbageCollector known it can safely cleanup.
Also this may address the behavior. http://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html
Upvotes: 0