I read this week about SAP publishing a free Memory Analyzer. I first tried it on a OutOfMemory heap dump of an commercial formatter which we use in that project that consumed so much of my time lately. It was an immediate eye opener.
So I wanted to play with this really great tool a little more and ran a big document which I knew would cause an OutOfMemoryError at 64MB heap size. Here's what came out: Class Name |Objects |Shallow Heap |Retained Heap |Perm --------------------------------------------------------------------------------------------------------------------- org.apache.fop.fo.properties.CondLengthProperty | 315'760| 7'578'240| 7'578'240| java.util.WeakHashMap$Entry | 121'873| 4'874'920| 8'861'568| org.apache.fop.fo.flow.Block | 26'391| 4'855'944| 54'554'584| org.apache.fop.fo.properties.SpaceProperty | 87'592| 4'204'416| 7'160'880| org.apache.fop.fo.properties.CommonBorderPaddingBackground | 78'940| 3'789'120| 16'419'968| org.apache.fop.fo.properties.KeepProperty | 110'661| 3'541'152| 3'541'152| org.apache.fop.fo.FOText | 30'461| 2'924'256| 52'488'224| org.apache.fop.fo.properties.CommonFont | 56'859| 2'729'232| 3'638'976| org.xml.sax.helpers.LocatorImpl | 109'431| 2'626'344| 2'626'344| org.apache.fop.fo.flow.TableCell | 23'154| 2'593'248| 54'296'024| org.apache.fop.fo.properties.CondLengthProperty[] | 78'940| 2'526'080| 10'102'592| org.apache.fop.fo.properties.CommonBorderPaddingBackground$BorderInfo[]| 78'940| 2'526'080| 2'526'080| org.apache.fop.fo.properties.NumberProperty | 98'304| 2'359'296| 3'932'184| org.apache.fop.fo.properties.CommonHyphenation | 56'852| 2'274'080| 2'274'080| char[] | 63'420| 1'931'488| 1'931'488| org.apache.fop.fo.properties.LengthRangeProperty | 37'843| 1'513'720| 1'513'736| org.apache.fop.fo.flow.TableColumn | 14'687| 1'409'952| 4'829'912| org.apache.fop.fo.properties.CommonMarginBlock | 30'592| 1'223'680| 4'160'448| org.apache.fop.fo.FONode[] | 44'346| 1'064'304| 55'383'904| org.apache.fop.fo.properties.PercentLength | 26'402| 1'056'080| 1'689'728| java.lang.Double | 57'931| 926'896| 926'968| java.lang.String[] | 57'188| 920'096| 943'640| org.apache.fop.fo.properties.CommonRelativePosition | 26'391| 844'512| 844'512| java.lang.String | 33'367| 800'808| 1'794'048| java.lang.Integer | 45'526| 728'416| 729'032| 2'221 more... | | | | Total of 2'246 entries |1'818'755| 67'134'104| | --------------------------------------------------------------------------------------------------------------------- Immediately shows that Andreas' recent change (rev 554091) for switching off the SAX Locators can save a lot of memory. I remembered Andreas working on a property cache a couple of weeks ago. Seems to work fine on EnumProperty: Look for class: .*EnumProperty.* Class Name |Objects |Shallow Heap |Retained Heap |Perm -------------------------------------------------------------------------------------------- org.apache.fop.fo.properties.EnumProperty$Maker| 94| 4'512| | org.apache.fop.fo.properties.EnumProperty | 182| 4'368| | Total of 2 entries | 276| 8'880| | -------------------------------------------------------------------------------------------- Then I noticed that NumberProperty (which uses the property cache) has 98304 instances which couldn't be right. The problem: just using that WeakHashMap isn't enough without the key objects implementing hashCode() and equals() (see JDK javadocs). So I added an implementation of the two methods to NumberProperty and here's what results: After the change: Look for class: .*NumberProperty.* Class Name |Objects |Shallow Heap |Retained Heap |Perm ------------------------------------------------------------------------------------------------------- org.apache.fop.fo.properties.NumberProperty$Maker | 34| 1'632| | org.apache.fop.fo.properties.NumberProperty | 10| 240| | org.apache.fop.fo.flow.TableFObj$ColumnNumberPropertyMaker| 1| 48| | Total of 3 entries | 45| 1'920| | ------------------------------------------------------------------------------------------------------- Saves 2.3MB of memory for the instances of just one class!!!! What I didn't look into is the penalty on performance, though. I can imagine that could be quite a bit. Here's the diff for the change: Index: C:/Dev/FOP/main/xml-fop-patching/src/java/org/apache/fop/fo/properties/NumberProperty.java =================================================================== --- C:/Dev/FOP/main/xml-fop-patching/src/java/org/apache/fop/fo/properties/NumberProperty.java (revision 557343) +++ C:/Dev/FOP/main/xml-fop-patching/src/java/org/apache/fop/fo/properties/NumberProperty.java (working copy) @@ -217,4 +217,33 @@ return Color.black; } + /** [EMAIL PROTECTED] */ + public int hashCode() { + //Multiply by 100 to get better buckets for Double instances + return (int)(number.doubleValue() * 100); + } + + /** [EMAIL PROTECTED] */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final NumberProperty other = (NumberProperty)obj; + if (number == null) { + if (other.number != null) { + return false; + } + } else if (!number.equals(other.number)) { + return false; + } + return true; + } + + } Food for thought... Jeremias Maerki