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(); }
|