【问题标题】:Is there a way to generate a periodic java thread dump using JVMTI?有没有办法使用 JVMTI 生成定期的 Java 线程转储?
【发布时间】:2015-01-03 21:45:14
【问题描述】:

在 java 中有多种生成线程转储的方法。

我想使用JVMTI(C API)来生成它,以评估它对正在运行的 JVM 的性能影响。 (我知道 jstack 和 JMX ;这个问题通常不是关于获取线程转储,而是关于使用 JVMTI API)。

我的代码基于this blog post。在那里,java 代理附加到 SIGQUIT 信号。我想避免这种情况,因为这与 JVM 用于将线程转储写入标准输出的信号相同。我想避免这种重复。

换句话说,我想附加到不同的信号,或者想办法让代理定期生成线程转储。

【问题讨论】:

  • jvmtiEventCallbacks 中的所有事件看起来都不合适(除非你想使用 DataDumpRequestion,但如果你这样做了,你就不会在这里问了:))。看起来您最好让您的代理直接致电GetStackTrace。你有什么理由不能这样做吗?
  • @Paul-Hicks,您能否发布关于在这种情况下如何附加到 jvm 的链接或代码?
  • 您将代理 dll 或 .so 放在 jvm 的 -agentpath 中或将其定义为 -agentlib。看看this answerIBM's intro page。这是你想知道的吗?我应该把它变成答案吗?
  • 好的,这就是我到目前为止连接到 jvm 的方式(略有不同)。所以我想这让我想到了下一个问题:如果不是从回调中调用 GetStackTrace,我如何获得指向 jvmtiEnv 的指针?关键是定期调用该函数。

标签: java jvmti thread-dump


【解决方案1】:

在那里,java 代理附加到 SIGQUIT 信号。我想避免这种情况,因为这与 JVM 用于将线程转储写入标准输出的信号相同。我想避免这种重复。

只需从您的代码中删除以下 sn-p

/* Set callbacks and enable event notifications */
memset(&callbacks, 0, sizeof(callbacks));
callbacks.DataDumpRequest = &dumpThreadInfo;
err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
CHECK_JVMTI_ERROR(jvmti, err);
err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,JVMTI_EVENT_DATA_DUMP_REQUEST, NULL);
CHECK_JVMTI_ERROR(jvmti, err);

我想附加到不同的信号

Here is论文,有点旧,但信息应该还是相关的。

只是一个如何进行信号处理的示例

import sun.misc.Signal;
import sun.misc.SignalHandler;

public class ThreadDumpSignalHandler implements SignalHandler {
    private volatile SignalHandler old;
    private ThreadDumpSignalHandler() {

    }
    public static void register(String sigName) {
        ThreadDumpSignalHandler h = new ThreadDumpSignalHandler();
        h.old = Signal.handle(new Signal(sigName), h)
    }
    public void handle(Signal sig) {
        threadDump();

        if(old != null && old != SIG_DFL && old != SIG_IGN) {
            old.handle(sig);
        }
    }
    // call your own threadDump native method.
    // note that in the implementation of this method you are able to access jvmtiEnv from *gdata (see below)
    private native void threadDump();
}

ThreadDumpSignalHandler.register("INT");

当然你可以编写完全原生的信号处理程序(请注意我没有测试过,这只是一个应该可行的想法)

static sighandler_t old_handler;
static void thread_dump_handler(int signum) {
    if(gdata && gdata->jvmti) {
        ... get thread dump
    }

    if(old_handler) {
        old_handler(signum);
    }
}

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
    old_handler = signal(SIGINT, thread_dump_handler);

    ...
}

或者找到一种方法让代理定期生成线程转储。

在您的示例中有全局 *gdata

typedef struct {
   /* JVMTI Environment */
   jvmtiEnv      *jvmti;
   jboolean       vm_is_started;
   /* Data access Lock */
   jrawMonitorID  lock;
} GlobalAgentData;

static GlobalAgentData *gdata;

...所以,您可以随时从那里获取 jvmtiEnv(计时器回调等)

【讨论】:

  • *gdata 获取*jvmti 安全吗?我不太确定,但我可以试一试。
  • 应该是安全的。至少您始终可以将RawMonitorEnterRawMonitorExit 与gdata->lock 一起使用,但我认为在您同时访问gdata->jvmti 之前没有必要这样做。
  • 另外,您可以通过RunAgentThread 创建一个线程。线程的start function 接受 jvmtiEnv 作为其第一个参数。因此,您可以在这个本地创建的线程中定期进行线程转储。
  • 使用 *jvmti 是 100% 安全的。生命周期指定为直到代理卸载。
  • 我还建议不要使用信号 - JVM 在内部混杂了许多信号,而且信号链很难正确处理。找到不同的触发机制(就像我的回答所暗示的那样) - 例如:套接字、信号量等......
【解决方案2】:

如果您的目标是定期收集线程转储,您可以使用 Java Flight Recorder,它是 Java Mission Controller 的一部分

从 Oracle JDK 7 Update 40 (7u40) 的发布开始,Java Mission Control 与 HotSpot JVM 捆绑在一起。

【讨论】:

    【解决方案3】:

    您引用的博客条目包含 JVMTI 管道所需的大部分内容。您可以使用 gdata 中的 JVMTIenv。那是合法的。确保您是否在进行 JNI 调用以使当前线程具有正确的 JNIenv。

    您现在需要添加一种方法来获得通知以执行您的操作(例如:线程转储)。启动一个在套接字上侦听的线程,使用inotify,命名信号量等。 - 你可以从外面戳的东西。

    然后,您可以根据需要从事件处理程序循环中调用 dumpThreadInfo()。

    【讨论】:

      【解决方案4】:

      jvmtiEventCallbacks 中的所有事件看起来都不合适(除非你想使用 DataDumpRequestion,但如果你这样做了,你就不会在这里问了:好的,所以这就是我到目前为止附加到 jvm 的方式(使用细微差别)。所以我想这让我想到了下一个问题:如果不是从回调中调用 GetStackTrace,我如何获取指向 jvmtiEnv 的指针?关键是调用 t

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-08
        • 2011-12-13
        • 2021-09-12
        相关资源
        最近更新 更多