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永远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由"java_re"与gcc构建4.3.0 20080428(红色帽子4.3.0-8)
(和Log4j 2.10.0)
JFR存在问题.它异步收集堆栈跟踪,即不仅像许多其他分析器那样在安全点.一方面,这使得采样更加真实,因为没有安全点偏差.另一方面,HotSpot JVM无法在任何随机时刻正确地遍历堆栈.
HotSpot私有API AsyncGetCallTrace尝试最好地从定时器信号中断应用程序的任意点恢复当前堆栈跟踪.但是,代码中的某些位置对于堆栈遍历并不安全.如果AsyncGetCallTrace在其中一个地方调用它,它可能会返回无效的堆栈跟踪或根本没有堆栈跟踪.
这是一个众所周知的问题AsyncGetCallTrace.JDK-8022893有问题的例子和分析.这种情况经常发生AsyncGetCallTrace(并且所有基于它的分析器)返回不准确的跟踪,其中某些帧被邻居方法错误地替换.