markspace said:
Why? Because the garbage collector never needs to remove the object.
You're using 20mb out of maybe 100mb to 1000mb or more? So the garbage
collector looks at that and says "plenty o' room left!" and doesn't run.
It's weird if you're used to having explicit control over object
destruction, but it works. More importantly it's efficient. Waiting,
and then removing lots of objects at once, is actually best for
throughput in the long run.
If you really need different behavior, check out Oracle's documentation
on tuning the garbage collector. You can choose what I believe is
called an incremental garbage collector which will work more like what
you are thinking.
<
http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html>
Found via Google with search terms "java garbage collection tuning".
Learn to STFW.
Shut the front window?
Both oracle.com and
https://www.ibm.com/developerworks/java/ have
excellent articles and white papers on Java garbage collection, how it
works and how you can fine tune it.
Make sure you have a problem before you try to solve it, but it is
always useful to gather knowhow.
The default generational garbage collector works well, especially
for idiomatic Java. As Brian Goetz points out in his Developerworks
articles, the allocation cost in the heap is very tiny, on the order of
a dozen or so machine instructions. Deallocation is likewise not
very expensive for short-lived objects. Young objects live in the
young generation heap, and dead ones are ignored during
minor GCs.
If most objects are short-lived, as should be true if one follows
best practices for scoping variables, then most GC cycles will gather
about 3% of live objects, ignoring the 97% of unreachable ones.
This is a very fast copy and shouldn't burden your throughput
very much.
When objects survive enough minor GC cycles, they move to
the tenured generation, a different area of heap. These are only
collected during major GC cycles, which use different and somewhat
slower algorithms under default setup. The major cycles are the
ones we tend to notice. So if an object is going to survive to tenure,
aim to have it survive for a very long time thereafter. Otherwise
use idioms that help objects die young.
For example, dying young:
public class Example
{
Collection<Foo> foos = getSomeFoos();
public void run()
{
for (Foo foo : foos)
{
Bar bar = foo.obtainBar();
// do whatever with 'bar'
// 'bar' falls out of scope
// if nothing else points to the same object
// it is gone in the next minor GC
}
}
}
Dying old:
public class Example
{
Collection<Foo> foos = getSomeFoos();
Bar bar; // the elephant in the room
public void run()
{
for (Foo foo : foos)
{
bar = foo.obtainBar();
// do whatever with 'bar'
// 'bar' does not fall out of scope
// The last reference from the loop lasts as
// long as this instance does, and could
// tenure the 'Bar' it points to
}
}
}
So coding things the first way, which chance are is
more correct logically, tends to reduce the impact of
GC on performance. It tilts the GC towards very rapid,
almost negligible young-generation collections and
less toward the slower, more intrusive tenured-generation
collections.
Read widely and carefully. There are some notable urban
legends out there, such as the advice to set all references
to 'null' when finished with them. There are specific times
to do so, such as when a collection such as a 'Stack' is
holding hidden references ("packratting", or the Java version
of a memory leak). Otherwise it's just superstition.