使用 Serviceability Agent 多线程探测 JVM 产生 VM.initialize() was not yet called 问题

报错

用 Serviceability Agent 技术多线程探测运行中的 Java 进程时,报出了关于 JVM 的错误

本地报错

image

报错代码位置

image

服务器报错,可以看到有的线程是能跑的

image

原因

原因在于主线程提前关闭,如下图是主线程执行程序的入口

image

进入 excute 函数

image

可以看到在 start 函数执行完后,会执行 finally 里的 stop 方法,调用 agentdetach 方法,detach 方法如下

public synchronized boolean detach() throws DebuggerException {
	if (isServer) {
		throw new DebuggerException("Should not call detach() for server configuration");
	}
	return detachInternal();
}

detachInternal 方法

private boolean detachInternal() {
	if (debugger == null) {
		return false;
	}
	boolean retval = true;
	if (!isServer) {
		VM.shutdown();
	}
	......
	......
}

因为 isServerfalse,所以会执行 VM.shutdown() 关闭虚拟机。但是此时线程池中的线程还在运行,自然也无法探测虚拟机里的内容,所以会报错。

解决

因为 excute 函数的代码无法改动,所以我们只能想办法延迟主线程的结束时间,让虚拟机尽可能晚的关闭。

可以利用 Future 类的特性

// 获取类加载器链表的头节点
ClassLoaderData classLoaderDataHead = classLoaderDataGraph.getClassLoaderGraphHead();
List<Future<Boolean>> futureList = new ArrayList<>();

while (classLoaderDataHead != null) {

	System.out.println(classLoaderDataHead.getClassLoader());

	final Klass l = classLoaderDataHead.getKlasses();
	// 多线程
	futureList.add(executorService.submit(new KlassProcessor(l, visitor)));
	classLoaderDataHead = classLoaderDataHead.next();
}

try {
	for (Future<Boolean> future : futureList) {
		future.get(13, TimeUnit.SECONDS);
	}
} catch (Exception e) {
	throw new RuntimeException(e);
} finally {
	executorService.shutdownNow();
}

使用 get 函数,设定线程池执行任务的时间上限,因为遍历 futureListfor 循环是主线程执行的,所以在遍历完 futureList 之前是不会关闭虚拟机的,达成了延迟虚拟机关闭的目的。

posted @ 2024-02-05 14:16  Frodo1124  阅读(83)  评论(0编辑  收藏  举报