我有一个用Java制作的小型系统采样器项目。
我想找到一种方法从所有线程获取方法的方法执行时间(自我时间),类似于VisualVM。但是,我根本不想使用任何仪器。
所以我有两个主要问题,一个广泛的问题,以及一些稍微更具体的问题:
广泛的问题:有没有办法计算单独使用Java + JMX的方法的自我时间?如果是,您的实施准确度如何?
更具体到我的问题:在我的项目中,我可以通过对所有线程堆栈跟踪进行采样来获取每个方法花费的CPU时间,获取样本之间的增量CPU时间并将其应用于堆栈的顶部框架(在我的数据结构中)。
我可以根据这些数据和样本之间的长度来推断基本执行吗?
以下是我的代码的简化版本:
private static final ThreadMXBean MX = ManagementFactory.getThreadMXBean();
private long lastCpuTime;
private Map<Long, ThreadTimerData> threadCache = new HashMap<Long, ThreadTimerData>();
public void sample()
{
final ThreadInfo[] threadInfos = MX.getThreadInfo( MX.getAllThreadIds(), Integer.MAX_VALUE );
for( ThreadInfo threadInfo : threadInfos )
{
final long threadId = threadInfo.getThreadId();
ThreadTimerData data = threadCache.get(threadId); // Just assume we already have this in our Map.
final StackTraceElement[] trace = threadInfo.getStackTrace();
if( trace == null )
{
continue;
}
final long cpuTime = MX.getThreadCpuTime( threadId );
data.update(trace[0].getClassName() + "." + trace[0].getMethodName(), cpuTime - lastCpuTime); // Another map, holding the name string against the delta.
}
lastCpuTime = cpuTime;
}
以200毫秒的间隔调用示例方法(在它自己的线程内) - 这可以更改。
答案 0 :(得分:0)
我相信我找到了那些好奇的解决方案:
基本上,如果任何元素(堆栈帧上的方法)位于堆栈顶部,则它在技术上正在执行。我们需要测量这个元素的存在时间,因此我们以一定的间隔对堆栈跟踪进行采样。这也回答了我的第一个问题的第二部分,准确度取决于间隔 - 较短的间隔意味着更有意义的自我时间,因为在任何两个样本之间,另一个元素出现并在堆栈中消失的可能性较小。
但我稍微离题了,我们得到两个采样时间之间的时间差(理想情况下使用纳秒精度),这给了我们这个方法执行了多长时间。我们在几个样本之后聚合这个,直到方法停止执行(当元素离开堆栈跟踪时)或者只是调用其他东西(一个新元素被压入堆栈)。
一旦堆栈发生变化,我们就会重复这个过程。此外,堆栈上的所有内容都使用CPU时间。所有这一切的棘手部分是创建最有效的数据结构来存储,检索和更新堆栈中的方法。