0

我正在开发一个监控应用程序,它使用 Sigar 进行监控以监控不同类型的应用程序。Sigar 的一个问题是,在监视 Java 应用程序 (JVM) 的堆使用情况时,我只能得到最大堆大小,而不是 JVM 实际使用的堆大小。因此,我扩展了我的监控应用程序以使用 JMX 连接到 JVM 并检索 CPU 以及堆使用情况。到目前为止这工作正常,但我想尽可能地自动化一切,我不想启动我的所有应用程序,被监控,激活 JMX,而是在需要时使用以下代码动态激活它:

private void connectToJVM(final String pid) throws IOException, AgentLoadException, AgentInitializationException {
    List<VirtualMachineDescriptor> vms = VirtualMachine.list();
    for (VirtualMachineDescriptor desc : vms) {
        if (!desc.id().equals(pid)) {
            continue;
        }
        VirtualMachine vm;
        try {
            vm = VirtualMachine.attach(desc);
        } catch (AttachNotSupportedException e) {
            continue;
        }
        Properties props = vm.getAgentProperties();
        String connectorAddress = props.getProperty(CONNECTOR_ADDRESS);
        if (connectorAddress == null) {
            String agent = vm.getSystemProperties().getProperty("java.home") + File.separator + "lib"
                    + File.separator + "management-agent.jar";
            vm.loadAgent(agent);

            // agent is started, get the connector address
            connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
        }
        vm.detach();
        JMXServiceURL url = new JMXServiceURL(connectorAddress);
        this.jmxConnector = JMXConnectorFactory.connect(url);
    }
}

到目前为止这工作正常,但问题是我现在依赖于tools.jarJDK。我现在的问题是,我可以在运行时以某种方式检查路径中tools.jar是否可用并在可用JAVA_HOME时加载它吗?因为如果它不可用,我只想用 Sigar 进行正常监控,但如果它可用,我想使用 JMX 来监控 Java 应用程序。我的项目是一个 Maven 项目,我正在使用它maven-shade-plugin来创建一个包含所有依赖项的可执行 jar。

目前我正在使用我在互联网上发现的一个肮脏的黑客,它使用反射将tools.jar动态添加到系统类路径(如果存在)。但我想知道是否也可以以不同的方式进行操作?预先感谢您的支持。

4

3 回答 3

3

我在我的项目中做了类似的事情,看这里

tools.jar这个想法是通过路径中的不同 ClassLoader 加载您的实用程序类。

    File javaHome = new File(System.getProperty("java.home"));
    String toolsPath = javaHome.getName().equalsIgnoreCase("jre") ? "../lib/tools.jar" : "lib/tools.jar";

    URL[] urls = new URL[] {
            getClass().getProtectionDomain().getCodeSource().getLocation(),
            new File(javaHome, toolsPath).getCanonicalFile().toURI().toURL(),
    };

    URLClassLoader loader = new URLClassLoader(urls, null);
    Class<?> utilityClass = loader.loadClass("some.package.MyUtilityClass");

    utilityClass.getMethod("connect").invoke(null);
于 2016-03-29T20:41:12.453 回答
0

我最终使用的代码来自:org.gridkit.lab::jvm Attach Api

从该源代码中,您只需要一个文件:AttachAPI.java

    /**
     * 版权所有 2013 Alexey Ragozin
     *
     * 根据 Apache 许可证 2.0 版(“许可证”)获得许可;
     * 除非遵守许可,否则您不得使用此文件。
     * 您可以在以下网址获取许可证的副本
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * 除非适用法律要求或书面同意,否则软件
     *根据许可分发是在“原样”基础上分发的,
     * 不提供任何明示或暗示的保证或条件。
     * 请参阅许可证以了解特定语言的管理权限和
     * 许可证下的限制。
     */
    包 org.gridkit.lab.jvm.attach;

    导入java.lang.reflect.Method;
    导入 java.net.URL;
    导入 java.net.URLClassLoader;

    /**
     * @作者 Alexey Ragozin (alexey.ragozin@gmail.com)
     */
    类 AttachAPI {

        私有静态最终 LogStream LOG_ERROR = LogStream.error();
        私有静态布尔值开始;

        静止的 {
            尝试 {
                String javaHome = System.getProperty("java.home");
                字符串 toolsJarURL = "file:" + javaHome + "/../lib/tools.jar";

                // 公开 addURL
                方法method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
                方法.setAccessible(true);

                URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
                if (sysloader.getResourceAsStream("/com/sun/tools/attach/VirtualMachine.class") == null) {
                    method.invoke(sysloader, (Object) new URL(toolsJarURL));
                    Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.VirtualMachine");
                    Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.AttachNotSupportedException");
                }

            } 捕捉(异常 e){
                LOG_ERROR.log("Java home 指向 " + System.getProperty("java.home") + " 确保它不是 JRE 路径");
                LOG_ERROR.log("添加 tools.jar 到类路径失败", e);
            }
            开始=真;
        };

        公共静态无效确保工具Jar(){
            如果(!开始){
                LOG_ERROR.log("附加 API 未初始化");
            }
        }   
    }

要使用此类,请将其放在项目中的某个位置,并确保相应地更改其包。在下面的示例中,我将文件放在与 MyApp.java 文件相同的文件夹中,但我没有修改 AttachAPI.java 文件的 package 语句以反映这一点,因为我想让它保持原始状态。

最后,在你的主类中,确保你有一个如下的块:

    公共类 MyApp
    {
        静止的 {
            AttachAPI.ensureToolsJar();
        }

        公共静态无效确保工具Jar(){
            // 什么都不做,只保证调用静态初始化器
        }



    }

    ...

现在您将不再需要在命令行上指定 tools.jar 的路径,并且只需一个java -jar MyApp.jar

于 2017-06-20T18:15:48.957 回答
0

在文件系统上查找 tools.jar 比 @apangin 的解决方案要复杂一些。不同的JDK将tools.jar贴在不同的地方,如这个方法所示,声称支持IBM JDK和Mac上的HotSpot。

但即使是我引用的代码看起来也已经过时了。它建议所有 mac JDK 使用 classes.jar,但我的 Mac 1.7 和 1.8 JDK 使用 tools.jar。

的另一个答案显示了一些 1.6、1.7 和 1.8 JDK 的 mac 的 tools.jar 和 classes.jar 文件的位置。

于 2016-12-14T09:50:13.020 回答