使用 Serviceability Agent 多线程探测 JVM 产生 VM.initialize() was not yet called 问题
报错
用 Serviceability Agent 技术多线程探测运行中的 Java 进程时,报出了关于 JVM 的错误
本地报错
报错代码位置
服务器报错,可以看到有的线程是能跑的
原因
原因在于主线程提前关闭,如下图是主线程执行程序的入口
进入 excute
函数
可以看到在 start
函数执行完后,会执行 finally
里的 stop
方法,调用 agent
的 detach
方法,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();
}
......
......
}
因为 isServer
为 false
,所以会执行 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
函数,设定线程池执行任务的时间上限,因为遍历 futureList
的 for
循环是主线程执行的,所以在遍历完 futureList
之前是不会关闭虚拟机的,达成了延迟虚拟机关闭的目的。