perf报告解读
Interpreting of perf report
我展示了使用 perf -g -p
收集的样本的 perf report
的输出。我不知道如何实现第一列。有很多 Java_*
个调用花费了 > 90% 的时间。
如何解读?
在显示的结果中有很多条目,我的意思是:
+ 98,78% 0,00% java libpthread-2.26.so
+ 95,77% 0,00% java libjvm.so
...
我的问题是:
为什么这样的条目多于 1 个?每个条目都是一种堆栈跟踪。单线程进程只有一个堆栈跟踪。
您在这里引用的列表:
+ 98,78% 0,00% java libpthread-2.26.so
+ 95,77% 0,00% java libjvm.so
...
And my question is:
Why is there more such entries than 1? Every entry is a kind of
stacktrace. The one-threaded process has exactly one stacktrace.
不是堆栈跟踪。它是在 运行 期间出现在任何样本的任何堆栈跟踪中的函数列表。只有当您展开该列表的一个元素时,您才能看到包含该函数的组合 tree-view 堆栈跟踪。
我不确定您所说的 one-threaded 进程只有一个堆栈跟踪 是什么意思。任何进程在其生命周期中都会有各种堆栈跟踪,即使它只有一个线程。例如,在它开始的那一刻,堆栈跟踪将只是 main()
,一旦 main()
调用一个函数,堆栈跟踪就会更改为包含该函数,依此类推。
现在在函数列表中,第一列显示总开销,包括所有 children(即包括此函数调用的函数)。由于几乎所有有趣的工作都发生在共享相同最外层函数的调用链中,因此顶层是一种无用的列表,其中显示的许多函数占 >90% 的开销。
第二列是"own"开销,表示在该方法中实际花费的时间1,不是 任何 children。对于所有顶级方法,这接近于零,因此真正的工作发生在这些方法调用的某些方法中,而不是这些方法本身。
您在顶部展开的树视图确实告诉了您需要了解的内容:您花费了 ~94% 的时间 inside/below radek.queue.wlQueue.writeBytes()
,而那个人花费了大部分时间它的时间在 String.intern()
。所以这里的瓶颈是所有的 String.intern()
调用,可能是因为字符串 table 太小,或者只是因为 String.intern()
只是 generally sucks for de-duplication。我这里的一般规则是只使用 Guava 的 Interner<String>
除非你特别需要 属性 文字字符串与 interned 字符串共享相同的字符串池(即 "foo" == new String("foo").intern()
)。
1 我在这里松散地说 "amount of time" - 它实际上是您指定给 perf record
的任何事件的总样本的一部分 - 但默认情况下那应该是大约 CPU 时间。
我展示了使用 perf -g -p
收集的样本的 perf report
的输出。我不知道如何实现第一列。有很多 Java_*
个调用花费了 > 90% 的时间。
如何解读?
在显示的结果中有很多条目,我的意思是:
+ 98,78% 0,00% java libpthread-2.26.so
+ 95,77% 0,00% java libjvm.so
...
我的问题是:
为什么这样的条目多于 1 个?每个条目都是一种堆栈跟踪。单线程进程只有一个堆栈跟踪。
您在这里引用的列表:
+ 98,78% 0,00% java libpthread-2.26.so + 95,77% 0,00% java libjvm.so
...
And my question is:
Why is there more such entries than 1? Every entry is a kind of stacktrace. The one-threaded process has exactly one stacktrace.
不是堆栈跟踪。它是在 运行 期间出现在任何样本的任何堆栈跟踪中的函数列表。只有当您展开该列表的一个元素时,您才能看到包含该函数的组合 tree-view 堆栈跟踪。
我不确定您所说的 one-threaded 进程只有一个堆栈跟踪 是什么意思。任何进程在其生命周期中都会有各种堆栈跟踪,即使它只有一个线程。例如,在它开始的那一刻,堆栈跟踪将只是 main()
,一旦 main()
调用一个函数,堆栈跟踪就会更改为包含该函数,依此类推。
现在在函数列表中,第一列显示总开销,包括所有 children(即包括此函数调用的函数)。由于几乎所有有趣的工作都发生在共享相同最外层函数的调用链中,因此顶层是一种无用的列表,其中显示的许多函数占 >90% 的开销。
第二列是"own"开销,表示在该方法中实际花费的时间1,不是 任何 children。对于所有顶级方法,这接近于零,因此真正的工作发生在这些方法调用的某些方法中,而不是这些方法本身。
您在顶部展开的树视图确实告诉了您需要了解的内容:您花费了 ~94% 的时间 inside/below radek.queue.wlQueue.writeBytes()
,而那个人花费了大部分时间它的时间在 String.intern()
。所以这里的瓶颈是所有的 String.intern()
调用,可能是因为字符串 table 太小,或者只是因为 String.intern()
只是 generally sucks for de-duplication。我这里的一般规则是只使用 Guava 的 Interner<String>
除非你特别需要 属性 文字字符串与 interned 字符串共享相同的字符串池(即 "foo" == new String("foo").intern()
)。
1 我在这里松散地说 "amount of time" - 它实际上是您指定给 perf record
的任何事件的总样本的一部分 - 但默认情况下那应该是大约 CPU 时间。