JDK-6767959 : Catalog resolver transient memory usage high
  • Type: Bug
  • Component: xml
  • Sub-Component: jaxp
  • Affected Version: 6u10
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: solaris_2.5.1
  • CPU: sparc
  • Submitted: 2008-11-05
  • Updated: 2012-06-08
  • Resolved: 2009-01-12
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6 JDK 7
1.4.0Fixed 6u11-rev b06Fixed 7Fixed
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
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(); } /**
26-01-2009

EVALUATION used StringBuffer instead of string.
19-12-2008