我正在开发一个监控应用程序,它使用Sigar进行监控来监控不同类型的应用程序。Sigar的一个问题是,在监视Java应用程序(JVM)的堆使用情况时,我只得到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
动态添加到系统类路径中(如果存在的话)。但我想知道是否也可以采取不同的做法?提前感谢您的支持。
我在我的项目中也做了类似的事情,看看这里。
这个想法是通过不同的ClassLoader加载您的实用程序类,该ClassLoader的路径中有tools.jar
。
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);
在文件系统上查找tools.jar比@angin的解决方案稍微棘手一些。不同JDK将tools.jar粘贴在不同的位置,如该方法所示,该方法声称支持Mac上的IBMJDK和HotSpot。
但即使是我引用的代码看起来也已经过时了。它建议所有macJDK使用classes.jar,但我的Mac1.7和1.8JDK使用tools.jar。
我的另一个答案显示了适用于mac的tools.jar和classes.jar文件的位置,分别是1.6、1.7和1.8 JDK。
我最终使用的代码来自: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
/***版权所有2013 Alexey Ragozin**根据Apache许可证2.0版许可("许可证");*除非符合许可证的规定,否则您不得使用此文件。*您可以在以下网址获取许可证副本:**http://www.apache.org/licenses/LICENSE-2.0**除非适用法律要求或书面同意,软件*根据许可证进行的分发是在"按原样"的基础上进行的,*无任何明示或暗示的保证或条件。*有关管理权限的特定语言,请参阅许可证*许可下的限制。*/包org.ridkit.lab.jvm.attach;导入java.lang.reflect.Method;导入java.net.URL;导入java.net.URLClassLoader;/***@作者Alexey Ragozin(alexey.ragozin@gmail.com)*/类AttachAPI{private static final LogStream LOG_ERROR=LogStream.ERROR();私有静态布尔值已启动;静态的{尝试{字符串javaHome=System.getProperty("java.home");字符串toolsJarURL="file:"+javaHome+"/../lib/tools.jar";//公开addURL方法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.ioke(sysloader,(Object)new URL(toolsJarURL));Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.VirtualMachine");Thread.currentThread().getContextClassLoader().loadClass("com.sun.tools.attach.AttachNotSupportedException");}}catch(异常e){LOG_ERROR.LOG("Java home指向"+System.getProperty("Java.home")+"确保它不是JRE路径");LOG_ERROR.LOG("未能将tools.jar添加到类路径",e);}started=true;};公共静态void ensureToolsJar(){if(!已启动){LOG_ERROR.LOG("附加API未初始化");}}}
若要使用此类,请将其放在项目中的某个位置,并确保相应地更改其包。在下面的示例中,我将该文件与我的MyApp.java文件放在同一文件夹中,但我没有修改AttachAPI.java文件的包声明来反映这一点,因为我想保持它的原始状态。
最后,在你的主类中,确保你有一个如下的块:
公共类MyApp{静态的{AttachAPI.ensereToolsJar();}公共静态void ensureToolsJar(){//什么都不做,只需确保调用静态初始值设定项}}。。。
现在,您将不再需要在命令行上指定tools.jar的路径,只需使用java -jar MyApp.jar
即可启动应用程序