从Stack Trace获取方法执行时间

时间:2017-09-12 15:19:56

标签: java jmx visualvm

我有一个用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毫秒的间隔调用示例方法(在它自己的线程内) - 这可以更改。

1 个答案:

答案 0 :(得分:0)

我相信我找到了那些好奇的解决方案:

基本上,如果任何元素(堆栈帧上的方法)位于堆栈顶部,则它在技术上正在执行。我们需要测量这个元素的存在时间,因此我们以一定的间隔对堆栈跟踪进行采样。这也回答了我的第一个问题的第二部分,准确度取决于间隔 - 较短的间隔意味着更有意义的自我时间,因为在任何两个样本之间,另一个元素出现并在堆栈中消失的可能性较小。

但我稍微离题了,我们得到两个采样时间之间的时间差(理想情况下使用纳秒精度),这给了我们这个方法执行了多长时间。我们在几个样本之后聚合这个,直到方法停止执行(当元素离开堆栈跟踪时)或者只是调用其他东西(一个新元素被压入堆栈)。

一旦堆栈发生变化,我们就会重复这个过程。此外,堆栈上的所有内容都使用CPU时间。所有这一切的棘手部分是创建最有效的数据结构来存储,检索和更新堆栈中的方法。