我正在开发一个监控应用程序,它使用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);
}
}
到目前为止这个工作正常,但问题是我现在已经从JDK的tools.jar
依赖了。
我现在的问题是,我可以在运行时检查tools.jar
路径中JAVA_HOME
是否可用,然后加载它?因为如果它不可用我只想用Sigar进行正常监视,但如果它可用,我想使用JMX来监视Java应用程序。
我的项目是一个maven项目,我使用maven-shade-plugin
创建一个包含所有依赖项的可执行jar。
目前,我正在使用我在互联网上发现的脏黑客,它使用反射将tools.jar
动态添加到系统类路径(如果存在)。但是我想知道是否有可能以不同的方式做到这一点?
在此先感谢您的支持。
答案 0 :(得分:3)
我在项目中做了类似的事情look here。
我们的想法是通过路径中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);
答案 1 :(得分:0)
在文件系统上查找tools.jar比@ apangin的解决方案更棘手。 不同的JDK将tools.jar粘贴在不同的地方,如this method所示,它声称支持Mac上的IBM JDK和HotSpot。
但即使我引用的代码看起来已经过时了。它建议所有mac JDK使用classes.jar,但我的Mac 1.7和1.8 JDK使用tools.jar。
这个other answer of mine显示了几乎1.6,1.7和1.8 JDK的tools.jar和classes.jar文件的位置。
答案 2 :(得分:0)
我最终使用的代码来自: org.gridkit.lab :: jvm Attach Api
https://mvnrepository.com/artifact/org.gridkit.lab/jvm-attach-api/1.2
源代码:http://central.maven.org/maven2/org/gridkit/lab/jvm-attach-api/1.2/
从该源代码中,您只需要一个文件: AttachAPI.java
/** * Copyright 2013 Alexey Ragozin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gridkit.lab.jvm.attach; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; /** * @author Alexey Ragozin (alexey.ragozin@gmail.com) */ class AttachAPI { private static final LogStream LOG_ERROR = LogStream.error(); private static boolean started; static { try { String javaHome = System.getProperty("java.home"); String toolsJarURL = "file:" + javaHome + "/../lib/tools.jar"; // Make addURL public Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); method.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"); } } catch (Exception e) { LOG_ERROR.log("Java home points to " + System.getProperty("java.home") + " make sure it is not a JRE path"); LOG_ERROR.log("Failed to add tools.jar to classpath", e); } started = true; }; public static void ensureToolsJar() { if (!started) { LOG_ERROR.log("Attach API not initialized"); } } }
要使用此类,请将其放在项目中的某个位置,并确保相应地更改其包。在下面的示例中,我将文件放在与MyApp.java文件相同的文件夹中,但我没有修改AttachAPI.java文件的软件包语句以反映这一点,因为我想让它保持原始状态。
最后,在您的主类中,确保您有一个块,如下所示:
public class MyApp { static { AttachAPI.ensureToolsJar(); } public static void ensureToolsJar() { // do nothing, just ensure call to static initializer } } ...
现在,您不再需要在命令行中指定tools.jar的路径,只需java -jar MyApp.jar