I've been made aware of a slow memory leak. I was able to identify
PropertyCache as the leaker. It appears as if the cache segments don't
get cleaned correctly. The majority of CacheEntry instances have a
cleared reference but don't get removed from the buckets. In time that
accumulates to millions of stale CacheEntries and a performance
degradation. I found that the less memory is available, the more stale
entries lie around because the JVM is quicker to free the weak
references. I experimented with SoftReferences but found that they were
a lot slower, and that wouldn't solve the clean-up problem anyway.

Here's some sample debug output I produced to observe the property
cache to give you an idea of what runtime looks like after only 30
seconds of repeatedly running readme.fo:

Bucket contents for org.apache.fop.fo.properties.EnumProperty
         -> 0%
Bucket contents for org.apache.fop.fo.properties.NumberProperty
       X -> 65%
Bucket contents for org.apache.fop.fo.properties.CommonFont
         -> 0%
Bucket contents for org.apache.fop.fo.properties.CommonHyphenation
     X   -> 80%
Bucket contents for org.apache.fop.fo.properties.KeepProperty
X        -> 80%
Bucket contents for org.apache.fop.fo.properties.StringProperty
XXXXXXXX -> 94%
Bucket contents for org.apache.fop.fo.properties.CondLengthProperty
   X  X  -> 80%
Bucket contents for org.apache.fop.fo.properties.CommonBorderPaddingBackground
      XX -> 80%
Bucket contents for org.apache.fop.fo.properties.EnumNumber
         -> 0%
Bucket contents for org.apache.fop.fo.properties.FixedLength
XxXXXXX  -> 70%
Bucket contents for org.apache.fop.fo.properties.FontFamilyProperty
 X X X   -> 85%

(an upper case X mean more than 50% stale entries in a bucket)

The problem is easily reproduced with MemoryEater in the test directory.

I've spent quite some time now looking for the best way to fix this but
I've only found solutions which have an unwanted impact on performance,
multi-threading problems or both. Or introduce the cleaner thread again.
Loading too much on the get() method is out of the question for
performance reasons. I'm running out of ideas.

Does anyone have some additional ones?

Thanks,
Jeremias Maerki

Reply via email to