While investigating a performance test result I found the following stack trace reported in Java Flight Recorder's "Hot Methods":
Stack Trace Sample Count Percentage(%)
----------- ------------ ----------
java.util.Arrays.rangeCheck(int, int, int) 358 2.212
java.util.Arrays.fill(char[], int, int, char) 358 2.212
java.lang.AbstractStringBuilder.setLength(int) 358 2.212
java.lang.StringBuilder.setLength(int) 358 2.212
org.apache.logging.log4j.core.async.RingBufferLogEvent.getMessageTextForWriting()
201 1.242
org.apache.logging.log4j.core.async.RingBufferLogEvent.setMessage(Message)
201 1.242
From the stack trace, it looks like setting the StringBuilder's length results in Arrays.fill
being called. However, I don't understand why this is happening because the StringBuilder's length is set to zero.
Looking at the code for Log4j's RingBufferLogEvent.getMessageTextForWriting
method, it is clear the StringBuilder's length is never set to any other value than zero:
// RingBufferLogEvent.java
private StringBuilder getMessageTextForWriting() {
if (messageText == null) {
messageText = new StringBuilder(Constants.INITIAL_REUSABLE_MESSAGE_SIZE); // 128
}
messageText.setLength(0); // <-- this call. Note: new length is ALWAYS zero.
return messageText;
}
I don't understand how this could ever result in Arrays.fill
being invoked. Looking at the code for AbstractStringBuilder
, Arrays.fill
should only be invoked if the new length is greater than the previous number of characters used.
// AbstractStringBuilder.java
public void setLength(int newLength) {
if (newLength < 0)
throw new StringIndexOutOfBoundsException(newLength);
ensureCapacityInternal(newLength);
if (count < newLength) { // <-- how can this condition be true if newLength is zero?
Arrays.fill(value, count, newLength, '\0');
}
count = newLength;
}
How could count < newLength
ever be true
when newLength
is always zero?
JVM Version:
Java HotSpot(TM) 64-Bit Server VM (25.144-b01) for linux-amd64 JRE (1.8.0_144-b01), built on Jul 21 2017 21:57:33 by "java_re" with gcc 4.3.0 20080428 (Red Hat 4.3.0-8)
(and Log4j 2.10.0)
Arrays#fill
indeed shouldn't be reached. – SeldanStringBuilder
? (Constants.INITIAL_REUSABLE_MESSAGE_SIZE
) – Briscoeif (count < newLength) {
? I mean if the condition really istrue
, I'd check where the values are coming from. – Briscoe-Xnocursedjvm
flag suggestion. The QA team will like that one. :-) – MerwynINITIAL_REUSABLE_MESSAGE_SIZE
could be overridden by thelog4j.initialReusableMsgSize
system property. Could you check if you aren't setting it to some negative number by mistake? (wild guess, I know, but running out of ideas here - at least ideas that don't involve dark magic, that is ;-)) – SeldanINITIAL_REUSABLE_MESSAGE_SIZE
is not overridden with a system property and even if it was a negative number,new char[capacity]
would fail in the AbstractStringBuilder constructor. – MerwynJIT
, thus I added the jvm tag. there are some really awesome users following that tag that could shed some light here - I hope u don't mind – Lietman