从 Stack Trace 获取方法执行时间
Getting method execution time from Stack Trace
我在 Java.
中制作了一个小型系统采样器项目
我想找个方法从所有线程获取方法的方法执行时间(self-time),类似于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 毫秒的间隔(在它自己的线程内)被调用 -- 这可以更改。
我相信我已经为那些好奇的人找到了解决方案:
本质上,如果任何元素(堆栈帧上的方法)位于堆栈顶部,它在技术上正在执行。我们需要测量这个元素在那里存在了多长时间,所以我们每隔一段时间对堆栈跟踪进行采样。这也回答了我的第一个问题的第二部分,准确性取决于间隔——较短的间隔意味着更有意义的自我时间,因为另一个元素在任何两个样本之间出现和消失在堆栈中的机会较小。
但我有点离题了,我们得到了两个采样时间之间的时间差(理想情况下使用纳秒精度),这给了我们该方法执行了多长时间。我们在几个样本之后聚合它,直到该方法停止执行(当元素离开堆栈跟踪时)或只是调用其他东西(一个新元素已被推入堆栈)。
一旦堆栈发生变化,我们就重复这个过程。此外,堆栈上的所有内容都使用 CPU 时间。所有这一切的棘手部分是创建最有效的数据结构来存储、检索和更新堆栈中的方法。
我在 Java.
中制作了一个小型系统采样器项目我想找个方法从所有线程获取方法的方法执行时间(self-time),类似于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 毫秒的间隔(在它自己的线程内)被调用 -- 这可以更改。
我相信我已经为那些好奇的人找到了解决方案:
本质上,如果任何元素(堆栈帧上的方法)位于堆栈顶部,它在技术上正在执行。我们需要测量这个元素在那里存在了多长时间,所以我们每隔一段时间对堆栈跟踪进行采样。这也回答了我的第一个问题的第二部分,准确性取决于间隔——较短的间隔意味着更有意义的自我时间,因为另一个元素在任何两个样本之间出现和消失在堆栈中的机会较小。
但我有点离题了,我们得到了两个采样时间之间的时间差(理想情况下使用纳秒精度),这给了我们该方法执行了多长时间。我们在几个样本之后聚合它,直到该方法停止执行(当元素离开堆栈跟踪时)或只是调用其他东西(一个新元素已被推入堆栈)。
一旦堆栈发生变化,我们就重复这个过程。此外,堆栈上的所有内容都使用 CPU 时间。所有这一切的棘手部分是创建最有效的数据结构来存储、检索和更新堆栈中的方法。