如何在Java进程中检查类是否已加载

3
我的任务是找出在Linux系统上运行的SpringBoot应用程序数量。所以我计划做以下事情: 1. 获取所有Java进程ID
$ ps -aux|grep java

2. 然后以某种方式从ID中获取已加载的类。

我不知道是否可能。请有心人指导我正确的方向吗? P.S.:我正在使用Java通过Process类运行上述命令,类似于以下内容:

Process process = Runtime.getRuntime().exec("ps -ef");

所以我想从一个Java项目中实现它。

不要使用老套的 ps ... | grep .... 命令。在Linux中,您可以至少使用 ps -fC java 列出所有执行 java 的进程。更好的方法是使用Oracle JDK自带的工具来列出系统上的Java进程:jps。然而,这个命令只会列出同一用户的进程。 - undefined
@gsl 我会调查一下jps,并尽快回复。 - undefined
4个回答

1
我是一名中文翻译助手,以下是翻译结果:

我来到这个页面是为了寻找一个食谱“如何查找一个罐子是否已经被装载”(这与原问题有点不同)。但也许对某些人会有用:

我发现jcmd是获取此信息的最佳方法,例如:

  1. 我有一个带有PID 1的运行中的JVM(它是在K8s中启动的CN应用程序):
$ jps 
1 MyAppLauncher
529 Jps
  1. 弄清楚 jcmd 在该JVM中提供了哪些选项:
[eceuser@ecs1-0 temp]$ jcmd 1 help
1:
The following commands are available:
JFR.stop
JFR.start
JFR.dump
JFR.check
VM.native_memory
VM.check_commercial_features
VM.unlock_commercial_features
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
VM.classloader_stats
GC.rotate_log
Thread.print
GC.class_stats
GC.class_histogram
GC.heap_dump
GC.finalizer_info
GC.heap_info
GC.run_finalization
GC.run
VM.uptime
VM.dynlibs
VM.flags
VM.system_properties
VM.command_line
VM.version
help

通过一些试错,我发现 VM.dynlibs 可以满足我的需求:
$ jcmd 1 VM.dynlibs | grep custom
7f59a4b3b000-7f59a4b3d000 r--s 00000000 fd:00 1116085962                 /home/app/lib/custom-fi-extensions-0.0.1-SNAPSHOT.jar

所以,是的,这个jar已经被加载了。

0
  1. 你需要在你的应用程序启动命令行中放入某种标识符。例如:

    String id = "com.app.demo";
    
  2. 当你运行你的应用程序时,确保包含这个字符串。假设你是从Java中启动它,请执行以下操作:

    Runtime.getRuntime().exec("C:\...\javaw.exe -cp ... -Dwhatever=" + id + " com.app.app.demo.Main");
    
  3. 通过id获取应用程序,类似于$ps aux | grep -i com.app.app.demo。在Java中调用Runtime.getRuntime().exec("ps aux | grep -i com.app.app.demo");

我更喜欢使用ProcessBuilder(从Java 8开始),并通过bash运行您的命令:

Process process = new ProcessBuilder().command("bash", "ps aux | grep com.app.app.demo").start()

希望这能给你一个想法。

我无法对作为进程运行的Java项目进行任何更改。因此,我无法对这些进程添加额外的内容并重新运行它们。 - undefined

0
假设您可以识别出某个特定的类,该类确定应用程序是否为Spring Boot应用程序,您可以使用JDK中的一些工具来输出已加载的类。我尝试过使用jcmd(您可以在jre/bin/目录下找到它):
我对bash不是很熟悉,所以可能不太高效,但是对于我来说这个方法有效,假设每个Spring Boot应用程序都加载了类org.springframework.boot.logging.LoggingInitializationContext,并且没有非Spring Boot应用程序加载它。
ps aux | grep [j]ava | awk '{print $2}' | xargs -I{} jcmd {} VM.class_hierarchy | grep "org.springframework.boot.logging.LoggingInitializationContext"

它查找包含java的进程,仅获取PID,并将其传递给jcmd,在其中使用grep搜索特定的类。输出似乎是正确的,唯一的问题是在这个长管道中我丢失了PID,并且不确定如何打印它。 但如果你的问题是“有多少个springboot应用程序”,那么这可能不是一个问题。我也不确定在使用jcmd VM.class_hierarchy时类是否会出现两次,但看起来并不像是这样。

注意:此方法适用于JDK 11。我先尝试了jmap,但由于某些原因在JDK 11上对我不起作用。


jcmd可能会对我有帮助,我会尽快试一试,然后回复你...谢谢 - undefined

0

快速而简单的方法:在你的类中添加一些静态块,并在其中打印一些内容到日志(或者系统输出)。静态块在类被类加载器加载时执行,而不是在第一个实例化时执行(这是在第一次调用构造函数时执行)。然后,如果你的打印输出在日志中,那么该类已经被加载。示例:
public class Sample { private static final Logger logger = ... static { logger.info("class Sample is loaded"); } ... }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接