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

How is it possible that StringBuilder.setLength(0) invokes Arrays.fill?

在调查性能测试结果时,我发现 Java 飞行记录器的 "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

从堆栈跟踪来看,似乎设置 StringBuilder 的长度会导致 Arrays.fill 被调用。但是,我不明白为什么会这样,因为 StringBuilder 的长度设置为 zero.

查看 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;
}

我不明白这怎么会导致 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, '[=14=]');
    }

    count = newLength;
}

newLength 始终为零时,count < newLength 怎么会变成 true


JVM 版本:

Java 适用于 linux-amd64 JRE (1.8.0_144-b01) 的 HotSpot(TM) 64 位服务器 VM (25.144-b01),构建于 2017 年 7 月 21 日 21:57:33 通过 "java_re" 使用 gcc 4.3.0 20080428 (Red Hat 4.3.0-8)

(和 Log4j 2.10.0)

JFR 有问题。它异步收集堆栈跟踪,即不仅在 many other profilers do 这样的安全点。一方面,这使得抽样更加真实,因为没有安全点偏差。另一方面,HotSpot JVM 无法在任何随机时刻正确遍历堆栈。

HotSpot private API AsyncGetCallTrace 尝试尽最大努力从定时器信号中断应用程序的任意点恢复当前堆栈跟踪。但是,代码中的某些地方对于堆栈遍历来说并不安全。如果在这些地方之一调用 AsyncGetCallTrace,它可能 return 无效堆栈跟踪或根本没有堆栈跟踪。

这是 AsyncGetCallTrace 的一个已知问题。 JDK-8022893有例子和问题分析。 AsyncGetCallTrace(以及所有基于它的分析器)return不准确的跟踪是一种相当常见的情况,其中某些帧被错误地替换为相邻方法。