United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6767959 Catalog resolver transient memory usage high
JDK-6767959 : Catalog resolver transient memory usage high

Details
Type:
Bug
Submit Date:
2008-11-05
Status:
Resolved
Updated Date:
2012-06-08
Project Name:
JDK
Resolved Date:
2009-01-12
Component:
xml
OS:
solaris_2.5.1
Sub-Component:
jaxp
CPU:
sparc
Priority:
P2
Resolution:
Fixed
Affected Versions:
6u10
Fixed Versions:
6u11-rev (b06)

Related Reports
Backport:
Backport:
Backport:

Sub Tasks

Description
During memory analysis, we found high transient memory usage (8MB)
in the Catalog.normalizeURI usage of StringBuilder.append:

4.5% - 7,841 kB - 57,052 alloc.
com.sun.org.apache.xml.internal.resolver.Catalog.resolveSystem
 4.4% - 7,735 kB - 56,164 alloc.
com.sun.org.apache.xml.internal.resolver.Catalog.normalizeURI
  1.9% - 3,253 kB - 7,486 alloc. java.lang.StringBuilder.append(char)
  1.0% - 1,765 kB - 8,670 alloc.
java.lang.StringBuilder.append(java.lang.String)
3.2% - 5,626 kB - 45,912 alloc.
com.sun.org.apache.xml.internal.resolver.Catalog.resolvePublic
 3.1% - 5,507 kB - 44,460 alloc.
com.sun.org.apache.xml.internal.resolver.Catalog.normalizeURI
  1.3% - 2,255 kB - 5,738 alloc. java.lang.StringBuilder.append(char)
  0.7% - 1,245 kB - 6,794 alloc.
java.lang.StringBuilder.append(java.lang.String)

The culprit is the following for loop that creates new StringBuilder instances with 0 size, so that each call to append creates a new StringBuilder instance, leaving the
old one on the heap, hence the high transient memory usage.


        for(int count = 0; count < bytes.length; count++)
        {
            int ch = bytes[count] & 0xff;
            if(ch <= 32 || ch > 127 || ch == 34 || ch == 60 || ch == 62 || ch
== 92 || ch == 94 || ch == 96 || ch == 123 || ch == 124 || ch == 125 || ch ==
127)
                newRef = (new
StringBuilder()).append(newRef).append(encodedByte(ch)).toString();
            else
                newRef = (new
StringBuilder()).append(newRef).append((char)bytes[count]).toString();
        }

Recommendation:
1. create StringBuilder with estimated capacity before the for loop


        StringBuilder sb = new StringBuilder(bytes.length);

        for(int count = 0; count < bytes.length; count++)
        {
            int ch = bytes[count] & 0xff;
            if(ch <= 32 || ch > 127 || ch == 34 || ch == 60 || ch == 62 || ch
== 92 || ch == 94 || ch == 96 || ch == 123 || ch == 124 || ch == 125 || ch ==
127)
                newRef =
sb.append(newRef).append(encodedByte(ch)).toString();
            else
                newRef =
sb.append(newRef).append((char)bytes[count]).toString();
        }

                                    

Comments
EVALUATION

used StringBuffer instead of string.
                                     
2008-12-19
SUGGESTED FIX

--- old/src/share/classes/com/sun/org/apache/xml/internal/resolver/Catalog.java	Fri Dec 19 10:02:14 2008
+++ new/src/share/classes/com/sun/org/apache/xml/internal/resolver/Catalog.java	Fri Dec 19 10:02:14 2008
@@ -2113,13 +2113,11 @@
    * @return The normalized URI reference.
    */
   protected String normalizeURI(String uriref) {
-    String newRef = "";
-    byte[] bytes;
-
     if (uriref == null) {
       return null;
     }
 
+    byte[] bytes;
     try {
       bytes = uriref.getBytes("UTF-8");
     } catch (UnsupportedEncodingException uee) {
@@ -2128,6 +2126,7 @@
       return uriref;
     }
 
+    StringBuffer newRef = new StringBuffer(bytes.length);
     for (int count = 0; count < bytes.length; count++) {
       int ch = bytes[count] & 0xFF;
 
@@ -2143,13 +2142,13 @@
 	  || (ch == 0x7C) // |
 	  || (ch == 0x7D) // }
 	  || (ch == 0x7F)) {
-	newRef += encodedByte(ch);
+	newRef.append(encodedByte(ch));
       } else {
-	newRef += (char) bytes[count];
+	newRef.append((char) bytes[count]);
       }
     }
 
-    return newRef;
+    return newRef.toString();
   }
 
   /**
                                     
2009-01-26



Hardware and Software, Engineered to Work Together