Hi Joachim,
"
Do you happen to also have something that helps finding out whether and/or when objects moves from newSpace to oldSpace (get tenured is the right term, I think)? "
Not really feasible except in debugging scenarios.
And even then, it is is problematic down low because an object's location in memory is constantly changing.
In our semi-space newspace that the "scavenger" works with, a live object will move from one space to the other as a scavenge is performed, and it will be in a different address each time.
An object keeps track of how often it has flipped, and once it reaches the maximum number of flips it will then move to one of the many segments (typically the main large segment) in old space.
At this point it will still probably move due to the compactor which slides objects towards lower memory addresses in a segment to fill in holes.
At some point it will likely stop moving as it, and the objects around it, are truly there for what is probably the duration of the application.
The main problem in tracking a particular object would be maintaining reasonable performance. Even the instructions to check if a notification should be sent or not in the GC at this location would cause a negative performance impact.
When I ported the old Visualage GC in assembly to C, I used to have to do a lot of this crazy stuff to make sure the algorithms were still working correctly.
When I added these little bits of tracking code here or there...I could literally watch my windows redraw and flash:)
Newspace size
- Since 9.0 we have adaptive tenuring which is supposed to help with these burst of activity. Instead of keeping the generation size "number of times flipped" a constant, this generational size can go up
or down depending on how full the semi-space is after a scavenge. The generations will fluctuate between 1-14.
- The scavenger only copies the live-set of objects...which is independent of the size of the full object set or the size of your newspace halves.
- Some considerations for newspace size are:
* You want to avoid tenuring objects too early....you won't be able to get at them again until a full GC. This favors making newspace bigger.
* CPU cache performance working with your active set. Its nice when newspace can fit in some level of the CPU cache. This favors make newspace smaller.
* You don't want to keep copying large amounts of long-lived data back and forth unnecessarily. Like multi-MB bitmap arrays. You can always pre-tenure very large data yourself (i.e. newTenured:) if you know its long-lived and its very big.
This is all application-profile dependent...and it can get complicated to attempt to reason about. For example, If you have smaller spaces but are copying in larger objects (i.e. like bitmap images), then lots of scavenges are happening to make room for it. This in turn is tenuring other objects that maybe should not have been tenured in the first place. This means you have a bunch of trash building up in old space that you will have to incur the cost for at some point down the road. In this scenario newspaceToSmall -> early tenuring -> unnecessary garbage in old space -> more global GC
On the other hand, if you make your semi-spaces too big and you have the same kind of profile with lots of larger long-lived objects, now you have a situation where they should have been tenured long ago, but now your application is having to copy huge amounts of data back and forth...back and forth....back and forth. In this scenario largeLiveSet -> newspaceToBig -> late tenuring -> unnecessary copying of objects every scavenge.
- Seth