代码里有频繁的日志打印,并且会打印大对象,单条日志较大,导致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调整最大容量。
本文介绍了Log4j2从2.9.0版本开始对日志打印优化,重点在于解决大对象日志导致内存溢出问题,通过修复`ParameterizedMessage`和`StringBuilder`的使用,避免了不必要的内存消耗。建议设置log4j.maxReusableMsgSize来控制日志消息的最大容量。
307

被折叠的 条评论
为什么被折叠?



