Search code examples
javajettyjava-17

Extremely high String.equalsIgnoreCase CPU usage


I'm running a fairly large Spring Boot web application with Jetty as the container. I have a filter that detects slow responses and logs some data about the request. This has been working very successfully for years but I have just added a check for whether the header name is "authorization" into the logging so that sensitive data is not logged in these cases and the whole CPU is used up on comparing strings. I have only on or two threads in this method and I don't see how this can be happening.

Old code with no problem:

Enumeration<String> headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
    String name = headers.nextElement();

    Enumeration<String> headerValues = request.getHeaders(name);
    while (headerValues.hasMoreElements()) {
        r.append(name);
        r.append(": ");
        r.append(headerValues.nextElement());
        r.append(System.lineSeparator());
    }
}

Problem code:

Enumeration<String> headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
    String name = headers.nextElement();

    Enumeration<String> headerValues = request.getHeaders(name);
    while (headerValues.hasMoreElements()) {
        if (!name.equalsIgnoreCase("authorization")) {
            r.append(name);
            r.append(": ");
            r.append(headerValues.nextElement());
            r.append(System.lineSeparator());
        }
    }
}

The time is spent in java.lang.StringLatin1.regionMatchesCI which should only be called for headers of length 13. In addition it seems that the time is spent in java.lang.Character.toLowerCase.

Total CPU for this application goes from around 10% to 98% with this change.


Solution

  • You are doing basically doing toLowerCase in a loop against the same value over and over again because hasMoreElements will always be true if you don't call nextElement.

    You could do it this way instead.

    Enumeration<String> headers = request.getHeaderNames();
    while (headers.hasMoreElements()) {
        String name = headers.nextElement();
    
        if (!name.equalsIgnoreCase("authorization")) {
            Enumeration<String> headerValues = request.getHeaders(name);
            while (headerValues.hasMoreElements()) {
                r.append(name);
                r.append(": ");
                r.append(headerValues.nextElement());
                r.append(System.lineSeparator());
        }
    }