今天我对我得到的Visual VM分析结果感到困惑。
我有以下简单的Java方法:
public class Encoder {
...
private BitString encode(InputStream in, Map<Character, BitString> table)
throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
BitString result = new BitString();
int i;
while ((i = reader.read()) != -1) {
char ch = (char) i;
BitString string = table.get(ch);
result = result.append(string);
}
return result;
}
}
此方法一次一个地从流中读取字符。对于每个字符,它查找它的位串表示,并且它连接这些位串以表示整个流。
BitString是一种自定义数据结构,它使用基础字节数组表示一系列位。
该方法表现非常糟糕。问题在于BitString#append
- 该方法创建一个新的字节数组,从两个输入BitStrings复制这些位并将其作为新的BitString实例返回。
public BitString append(BitString other) {
BitString result = new BitString(size + other.size);
int pos = 0;
for (byte b : this) {
result.set(pos, b);
pos++;
}
for (byte b : other) {
result.set(pos, b);
pos++;
}
return result;
}
但是,当我尝试使用VisualVM来验证发生了什么时,我得到的是:
我对Visual VM和概要分析的经验很少。根据我的理解,这似乎问题在encode
方法本身而不是append
中存在。
可以肯定的是,我用自定义时间测量包围了整个编码方法和附加调用,如下所示:
public class Encoder {
private BitString encode(InputStream in, Map<Character, BitString> table)
throws IOException {
>> long startTime = System.currentTimeMillis();
>> long appendDuration = 0;
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
BitString result = new BitString();
int i;
>> long count = 0;
while ((i = reader.read()) != -1) {
char ch = (char) i;
BitString string = table.get(ch);
>> long appendStartTime = System.currentTimeMillis();
result = result.append(string);
>> long appendEndTime = System.currentTimeMillis();
>> appendDuration += appendEndTime - appendStartTime;
>> count++;
>> if (count % 1000 == 0) {
>> log.info(">>> CHARACTERS PROCESSED: " + count);
>> long endTime = System.currentTimeMillis();
>> log.info(">>> TOTAL ENCODE DURATION: " + (endTime - startTime) + " ms");
>> log.info(">>> TOTAL APPEND DURATION: " + appendDuration + " ms");
>> }
}
return result;
}
}
我得到了以下结果:
CHARACTERS PROCESSED: 102000
TOTAL ENCODE DURATION: 188276 ms
APPEND CALL DURATION: 188179 ms
这似乎与Visual VM的结果相矛盾。
我错过了什么?
答案 0 :(得分:1)
您正在看到此行为,因为VisualVM只能在安全点处对调用堆栈进行采样,并且JVM正在从代码中优化安全点。这导致样本在“自发时间”下集总在一起,从而使其人为夸大且产生误导。有两种可能的修复方法:
-XX:-Inline
和-XX:+UseCountedLoopSafepoints
。这些会使您的代码变慢一些,但会使分析结果更加准确。这个解决方案很简单,通常就足够了。只记得在不进行概要分析时删除这些选项!在您的特定情况下,JVM可能内联了对BitSting.append()
的方法调用以提高性能。这将导致通常在方法调用结束时删除的安全点,这意味着该方法将不再显示在事件探查器中。
有一篇很棒的博客文章here,其中有关于安全点是什么以及它们如何工作的更多详细信息,而另一本here则更详细地讨论了安全点和采样分析器之间的交互作用。
答案 1 :(得分:0)
[这个答案是无效的。但我保留它直到OP得到一个成员的帮助,因为这篇文章包含两个评论,帮助其他人理解问题]。
在这种情况下,VisualVM测量了实际的CPU时间,但您测量的时间值是&#34;经过的时间&#34;。
如果执行线程必须等待IO或网络,则该时间不会被测量为CPU时间。