记录大字符串会导致OutOfMemoryError

时间:2018-09-05 08:12:03

标签: java spring performance logging logback

我的Spring Boot Web应用程序连接到许多外部服务,并且需要将所有与服务之间的请求和响应写入日志文件。

我将Logback用于日志引擎。以下代码用于将响应消息打印到日志文件。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

....

private static final Logger LOG = LoggerFactory.getLogger(RequestLoggingClientRequestInterceptor.class);

...

ClientHttpResponse response = execution.execute(request, body);
InputStream s = response.getBody();
String loggingResponseBody = new String(ByteStreams.toByteArray(s), Charset.forName("UTF-8"));
LOG.info("response status code: {}, response headers: {}, response body: {}",
            response.getStatusCode(),
            response.getHeaders(),
            loggingResponseBody);

此代码存在性能问题。它消耗大量CPU,导致高延迟,并且在某些情况下,响应消息非常大,它会在执行OutOfMemoryError时导致new String(ByteStreams.toByteArray(s), Charset.forName("UTF-8"));

Caused by: java.lang.OutOfMemoryError: Java heap space
    at java.lang.StringCoding.decode(StringCoding.java:215)
    at java.lang.String.<init>(String.java:463)
    at java.lang.String.<init>(String.java:515)
    at ...

请注意,我没有将字符串解码为'UTF-8'的具体要求,我这样做是因为String类的构造函数建议。

请提出如何改进代码以解决性能问题的建议。我尝试过AsyncAppender,尽管它有助于缓解延迟,但我仍然坚持使用OutOfMemoryError

1 个答案:

答案 0 :(得分:2)

必须关闭ClientHttpResponse(按照指定),而ByteStreams.toByteArray(s)则不应该关闭(按照指定)。

try (InputStream s = response.getBody()) {
    LOG.info("response status code: {}, response headers: {}, response body:",
            response.getStatusCode(),
            response.getHeaders());
    String loggingResponseBody = new String(ByteStreams.toByteArray(s),
            StandardCharsets.UTF_8);
    LOG.info(loggingResponseBody); // One param means no format with {}
}

所以这可能只是资源泄漏。该代码似乎很脆弱,因为必须确定响应是UTF-8文本且不超过无意义的兆字节。