StringBuilder.setLength(0)怎么可能调用Arrays.fill?

Rem*_*pma 12 java arrays stringbuilder jvm

在调查性能测试结果时,我发现Java Flight Recorder的"热方法"中报告了以下堆栈跟踪:

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
Run Code Online (Sandbox Code Playgroud)

从堆栈跟踪中,看起来设置StringBuilder的长度导致Arrays.fill被调用.但是,我不明白为什么会发生这种情况,因为StringBuilder的长度设置为零.

查看Log4j RingBufferLogEvent.getMessageTextForWriting方法的代码,很明显StringBuilder的长度永远不会设置为零以外的任何其他值:

// 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;
}
Run Code Online (Sandbox Code Playgroud)

我不明白这是如何导致Arrays.fill被调用的.看一下代码AbstractStringBuilder,Arrays.fill应该只被调用,如果新的长度大于前一个号码使用的字符.

// 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;
}
Run Code Online (Sandbox Code Playgroud)

怎么能count < newLength永远truenewLength始终是零?


JVM版本:

用于linux-amd64 JRE(1.8.0_144-b01)的Java HotSpot(TM)64位服务器VM(25.144-b01),于2017年7月21日21:57:33由"java_re"与gcc构建4.3.0 20080428(红色帽子4.3.0-8)

(和Log4j 2.10.0)

apa*_*gin 9

JFR存在问题.它异步收集堆栈跟踪,即不仅像许多其他分析器那样在安全点.一方面,这使得采样更加真实,因为没有安全点偏差.另一方面,HotSpot JVM无法在任何随机时刻正确地遍历堆栈.

HotSpot私有API AsyncGetCallTrace尝试最好地从定时器信号中断应用程序的任意点恢复当前堆栈跟踪.但是,代码中的某些位置对于堆栈遍历并不安全.如果AsyncGetCallTrace在其中一个地方调用它,它可能会返回无效的堆栈跟踪或根本没有堆栈跟踪.

这是一个众所周知的问题AsyncGetCallTrace.JDK-8022893有问题的例子和分析.这种情况经常发生AsyncGetCallTrace(并且所有基于它的分析器)返回不准确的跟踪,其中某些帧被邻居方法错误地替换.