I made a new commit tonight that saved 7MB of heap in my benchmark (that is, about a 10% improvement). Here's the diff: https://github.com/chrisdolan/pcgen-svn/commit/eb274a4779c1d6157a43e46b9972e8d2493aa3cc
This change adds optional filters to the GameModeFileLoader and the CampaignFileLoader. The filter is a callback to user code, which can reject irrelevant data files. In my case, I do a quick-and-dirty parse of the .pcg file to get the CAMPAIGN and GAMEMODE lines, and reject all that don't match. The campaign loader already had some fix-up code that recursively loads other campaigns that the main one depends on.
Not surprisingly, the majority of that 7MB savings came from the campaign filter. The improvement from the game mode filter was tiny by contrast. Because the GameModeFileLoader messes with Globals more than the CampaignFileLoader, I'd recommend that other people do not use my game mode filter unless you're *sure* that all loaded PCs will be the same game mode.
My next benchmark will be Tom's idea to lazy load the HashMaps in the CDOM. Eclipse MAT predicts that I can save up to 13MB(!) by such techniques. The following table counts all of the HashMap instances in heap and sorts them by how many elements they contain. As you can see there are over 100,000 empty hashmaps. I don't yet know what fraction of those are in CDOM instances.
Length # Objects Shallow Heap Retained Heap
0 106,792 5,126,016 13,682,144
1 22,808 1,094,784 7,786,272
2 13,684 656,832 7,877,944
3 14,614 701,472 6,757,224
4 4,514 216,672 2,627,064
5 9,159 439,632 3,685,112
6 3,184 152,832 1,714,720
A related savings could come via tweaks to the fill ratio for the hashmaps. We could optimize for space instead of speed in some cases.