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

Reply via email to