代码里有频繁的日志打印,并且会打印大对象,单条日志较大,导致jvm
被大量日志对象占用。
log4j2
在2.9.0
版本代码
org.apache.logging.log4j.message.ParameterizedMessage
@Override
public String getFormattedMessage() {
if (formattedMessage == null) {
final StringBuilder buffer = getThreadLocalStringBuilder();
formatTo(buffer);
formattedMessage = buffer.toString();
StringBuilders.trimToMaxSize(buffer, Constants.MAX_REUSABLE_MESSAGE_SIZE);
}
return formattedMessage;
}
private static StringBuilder getThreadLocalStringBuilder() {
StringBuilder buffer = threadLocalStringBuilder.get();
if (buffer == null) {
buffer = new StringBuilder(DEFAULT_STRING_BUILDER_SIZE);
threadLocalStringBuilder.set(buffer);
}
buffer.setLength(0);
return buffer;
}
org.apache.logging.log4j.util.StringBuilders
public static void trimToMaxSize(final StringBuilder stringBuilder, final int maxSize) {
if (stringBuilder != null && stringBuilder.capacity() > maxSize) {
stringBuilder.setLength(maxSize);
stringBuilder.trimToSize();
}
}
在2.9.0
以下版本
@Override
@Override
public String getFormattedMessage() {
if (formattedMessage == null) {
final StringBuilder buffer = getThreadLocalStringBuilder();
formatTo(buffer);
formattedMessage = buffer.toString();
}
return formattedMessage;
}
private static StringBuilder getThreadLocalStringBuilder() {
StringBuilder buffer = threadLocalStringBuilder.get();
if (buffer == null) {
buffer = new StringBuilder(DEFAULT_STRING_BUILDER_SIZE);
threadLocalStringBuilder.set(buffer);
}
buffer.setLength(0);
return buffer;
}
在使用 log.info("test ->, {}", body)
代码时,log4j2
会走到getFormattedMessage
方法,由于低于2.9.0
版本的只会将length
设置为0
,对于stringbuilder
只是字符个数变为了0
,实际上其持有的字符还是在内存中,具体可以参考它的capacity
方法,stringbuilder
容量并没有改变。
在2.9.0
版本修复了此问题,stringbuilder
会被裁剪容量,最大容量被设置为(128 * 2 + 2) * 2 + 2
,通过裁剪避免日志占用大量内存导致oom
的问题。
2.9.0
之后可以通过设置log4j.maxReusableMsgSize
调整最大容量。