As the output shows, the JVM (HotSpot) started with 2MB of total memory, with
about 87% of free memory. As the program starts to allocate objects during the
initialization phase, free memory starts to decrease. I don't know why.
When the free memory decreased to 14%, about 294KB, still enough for 2 more
objects, the JVM decided to request more total memory from the operating system.
At second 8, the program finished the initialization phase, and entered into
the loop testing phase. It started to allocate one object, then release the oldest
object from the list. The released objects became dead objects and waiting to be
collected as garbage.
As you can see from the output, the dead objects were not immediately collected
by the garbage collector. The first collection of dead objects happened at second
14, when free memory decreased to 8%, about 275KB. The JVM collected the dead objects and
request more total memory at about the same time. I don't know why.
As the program continued to repeatedly allocate and release objects, dead objects
were accumulated again, and the free memory continued to decrease. When free memory
decreased to 3%, 197KB, at second 30, the JVM decided to collect dead objects and request more
total memory again.
The next time the JVM decided to collect dead objects and request more total memory
happened at second 56. Total memory reached 8128KB, about the same as the maximum
memory size specified by the command line option, -Xmx8m.
When the free memory decreased again to 3% at second 75, the JVM could not get any
more total memory. It only collected the dead objects, and raised the free memory
back to 41%.
While reviewing the rest of the output, I noticed that at second 100, the JVM
collected the dead objects with 3200KB of memory. However, the free memory reported
by the JVM only increased by 2389KB (2614KB-225KB). So where is the other 800KB
collected from the dead objects? Can any one explain this?
19 seconds later, the JVM collected only 2432KB of dead objects, but the free memory
increased by 3137KB. This time, about 700KB more memory comes out from nowhere.
May be certain amount of memory was reserved at the previous garbage collection
time.
After looking at the over head memory size, I found that each time the JVM collected
garbage, it also reserved the same amount of memory as the garbage collected.
This explains well why at second 100, 800KB of collected memory was not giving
back as free memory. At that time, 8128 (total memory) - 2176 (active objects)
- 3338 (reserved memory) = 2614 (free memory), where the reserved memory (3338KB)
is about the same as the collected memory (3200KB).
Couple of quick observations:
When the free memory is low, the JVM will try go request more total memory,
and collect garbages at the same time.
The JVM will let the free memory run as low as 2% of the total memory, before
collecting garbages.
At the end of garbage collection, the same amount of memory as the collected
garbages will be reserved.