Java VisualVM-奇怪的自我时间
今天我被我得到的可视化虚拟机评测结果弄糊涂了 我有以下简单的Java方法:Java VisualVM-奇怪的自我时间,java,profiling,visualvm,Java,Profiling,Visualvm,今天我被我得到的可视化虚拟机评测结果弄糊涂了 我有以下简单的Java方法: public class Encoder { ... private BitString encode(InputStream in, Map<Character, BitString> table) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(in)
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;
}
}
然而,当我尝试使用VisualVM验证发生了什么时,我得到了以下结果:
一般来说,我对可视化虚拟机和评测的经验很少。据我所知,问题似乎出在encode
方法本身的某个地方,而不是append
可以肯定的是,我将整个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;
}
}
这似乎与VisualVM的结果相矛盾
我遗漏了什么?[这个答案是无效的。但我会一直保留它,直到得到会员的帮助,因为这篇文章包含了两条注释,可以帮助其他人理解这个问题] 在本例中,VisualVM测量了实际的CPU时间,但测量的时间值是“经过的时间”
如果执行线程必须等待IO或网络,那么该时间将不会被测量为CPU时间。您会看到这种行为,因为VisualVM只能在安全点对调用堆栈进行采样,而JVM正在优化代码中的安全点。这导致样本被集中在“自我时间”下,这使得它被人为夸大和误导。有两种可能的修复方法:
- 为了使VisualVM更好地工作,添加JVM选项以保留更多安全点,如
和-XX:-Inline
。这将使代码速度降低一些,但会使分析结果更加准确。这个解决方案很简单,而且通常已经足够好了。只要记住在不进行评测时删除这些选项李>-XX:+UseCountedLoopSafepoints
- 如果您不介意切换探查器,可以使用或。它们能够使用JVM的特殊API在safepoints以外的地方采集样本。这个解决方案更准确一些,因为您正在评测完全优化的代码,但在我看来,这些评测器比VisualVM更难使用
BitString.append()
的方法调用以提高性能。这会导致通常位于方法调用末尾的safepoint被删除,这意味着该方法将不再显示在探查器中
有一篇精彩的博文,详细介绍了什么是safepoints以及它们是如何工作的,还有一篇博文更详细地介绍了safepoints和采样分析器之间的交互作用。您是如何测量时间的,你能不能也加上这个代码。我加上了时间测量的解释,还有
append
的代码。我认为没有任何IO或网络在那里等待。也许GC暂停了?你试过了吗?它可以很好地与Java配合使用,并将准确地告诉您时间的去向。注意,它不能测量。相反,它利用了一个事实,即该活动需要花费大量的时间来确定它的位置。您可以使用它来获得非常粗略的测量,但它可以精确地发现问题。请参阅我的扩展答案-我包括了append
的实现。我认为那里没有做任何IO。也许更喜欢暂停?但是,正如您在屏幕截图中看到的,VisualVM显示了两个值—Time
和Time(CPU)
。时间
不是经过的时间和时间(CPU)
是实际的CPU时间吗?是的,你是对的,这里有些奇怪,我的答案对你的情况无效。对于2次编码调用,append只被调用了4次。乍一看,所有调用都没有被记录。熟悉VisuvalVM的人可能会帮助您。根据我在JProfiler上的经验,我试图帮助你。谢谢
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