Re: Ranting about JVM's default memory limits...
Lew <noone@invalid.com> wrote:
In the process of copying live references from one young-generation
space to the other, the heap is automatically compacted.
Peter Duniho wrote:
What happens as objects are eventually removed from an older-generation
space?
Presumably some explicit compaction has to happen _somewhere_.
I don't know, but it's less of a problem for old-generation because allocation
doesn't happen there. Some googling should give you that answer quickly.
..NET achieves the same thing simply by putting compaction off until
it's really needed.
It's why it's better to use short-lived objects in Java programs.
In .NET, the memory manager simply keeps track of the boundary between
the generations (if I recall correctly, the current implementation has
just two generations: young and old). Either generation can become
fragmented, but what normally happens is that at the same time the young
generation is collected, anything left winds up in the old generation by
virtue of the boundary pointer being moved. So the old generation
inherits whatever fragmentation existed in the young generation.
Things don't move from young to tenured in Java until they have been through
several minor collections.
The tenured generation is a completely separate space from the young
generation. The "boundary pointer" doesn't move.
It surprises me to hear that Java's implementation isn't similar.
Not me.
Certainly there's an automatic compaction that would occur if references
actually are _copied_ from one generation to another, but that comes at
a cost: the memory manager is effectively required to compact with
_every_ collection that updates the generations (and presumably that's
every collection?). And no matter when compaction is happening (during
all collections or less frequently, on an "as-needed" basis), it's
expensive both because of all the data that has to be touched, and
because it requires threads to be suspended so that their references can
all be updated.
I don't know if this happens in the old generation in Java. Compaction
happens by the copy from one young space to the other in the young generation.
Since short-lived objects tend to dominate, being around 95-98% of objects
in most programs according to what I've read, not a whole lot need be copied.
It can be made worse by needlessly hanging on to objects in Java.
If Java's really doing all that work with every collection, I guess I
can see how the GC implementation in Java would have trouble any time
any significant number of objects ages out of the young generation,
since that would significantly increase the cost of each collection
operation.
That is a strong case for keeping objects short-lived in Java. Thus it is
better to use:
for ( Foo foo : foos )
{
Bar bar = new Bar( foo );
...
}
than
Bar bar = new Bar();
for ( Foo foo : foos )
{
bar.init( foo );
...
}
(assuming the state of a Bar is the same after 'new Bar(foo)' or 'init(foo)')
--
Lew