Looking at a heap dump in jhat (see #3314), I noticed some of the maps referenced by ContentEntry.state had > 6,000 entries, which doesn't make sense (we don't do THAT much concurrent access; we try to keep to one transaction at a time per feature type). Looking over the code again, it seems that ContentEntry.state is added to a lot, but its entries are never cleaned up anywhere. I looked high and low, but I couldn't find where the end of a transaction causes its state to be cleaned from ContentEntry. The only other possibility is that instances of ContentEntry themselves are recycled and recreated every now and then, but I couldn't find that either (ContentEntry.dispose() is only called when the data store itself is being disposed).
I'd like to submit a patch, but I'm not really sure where the appropriate place to clean up the state is. Probably when the Transaction is closed, but it would be good to get confirmation of that....