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

时间:2017-12-01 09:28:04

标签: java arrays jvm stringbuilder

在调查性能测试结果时,我发现Java Flight Recorder" 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的长度设置为

查看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, '\0');
    }

    count = newLength;
}

count < newLength始终为零时,true怎么可能newLength

JVM版本

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

(和Log4j 2.10.0)

1 个答案:

答案 0 :(得分:9)

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

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

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