JMX-JAVA进程监控利器
Java?管理擴(kuò)展(Java Management Extension,JMX)是從jdk1.4開始的,但從1.5時(shí)才加到j(luò)dk里面,并把API放到j(luò)ava.lang.management包里面。
如果一個(gè) Java 對(duì)象可以由一個(gè)遵循 JMX 規(guī)范的管理器應(yīng)用管理,那么這個(gè)Java 對(duì)象就可以稱為一個(gè)可由 JMX 管理的資源。
要使一個(gè) Java 對(duì)象可管理,則必須創(chuàng)建相應(yīng)的 MBean 對(duì)象,并通過這些 MBean 對(duì)象管理相應(yīng)的 Java 對(duì)象。當(dāng)擁有 MBean 類后,需要將其實(shí)例化并注冊(cè)到 MBeanServer 上。
一共有四種類型的 MBean , 分別是標(biāo)準(zhǔn)類型 MBean, 動(dòng)態(tài)類型 MBean, 開放類型 MBean 和模型類型 MBean。
?
注:
?
1、本機(jī)使用
當(dāng)我們啟動(dòng)java進(jìn)程后,經(jīng)常會(huì)使用jps,jinfo,jmap,jstat等jdk自帶的命令去查詢進(jìn)程的狀態(tài),這其中的原理就是,當(dāng)java進(jìn)程啟動(dòng)后,會(huì)創(chuàng)建一個(gè)用于本機(jī)連接的“l(fā)ocalConnectorAddress”放到當(dāng)前用戶目錄下,當(dāng)使用jps等連接時(shí),會(huì)到當(dāng)前用戶目錄下取到“l(fā)ocalConnectorAddress”并連接。
package com.dxz.study;import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set;import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL;import org.junit.Test;import com.sun.tools.attach.VirtualMachine; import com.sun.tools.attach.VirtualMachineDescriptor;public class JmxTest {@Test public void test1() { List<VirtualMachineDescriptor> vms = VirtualMachine.list(); for (VirtualMachineDescriptor desc : vms) { VirtualMachine vm; try { System.out.println("desc:" + desc); System.out.println("進(jìn)程id:"+desc.id()); vm = VirtualMachine.attach(desc); } catch (Exception e) { e.printStackTrace(); continue; } JMXConnector connector = null; try { Properties props = vm.getAgentProperties(); for (Map.Entry<Object, Object> entry : props.entrySet()) { System.out.println(entry.getKey() + "->" + entry.getValue()); } String connectorAddress = props.getProperty("com.sun.management.jmxremote.localConnectorAddress"); if (connectorAddress == null) { System.out.println("connectorAddress is null"); continue; } System.out.println("conn:" + connectorAddress); //以下代碼用于連接指定的jmx,本地或者遠(yuǎn)程 JMXServiceURL url = new JMXServiceURL(connectorAddress); //JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/TestJMXServer"); connector = JMXConnectorFactory.connect(url); MBeanServerConnection mbeanConn = connector.getMBeanServerConnection(); Set<ObjectName> beanSet = mbeanConn.queryNames(null, null); // ... } catch (Exception e) { e.printStackTrace(); } finally { try { if (connector != null) connector.close(); break; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.dxz</groupId><artifactId>study</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>study</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.1</version><scope>test</scope></dependency><dependency><groupId>org.glassfish.external</groupId><artifactId>opendmk_jdmkrt_jar</artifactId><version>1.0-b01-ea</version></dependency><dependency><groupId>org.jmockit</groupId><artifactId>jmockit</artifactId><version>1.24</version></dependency></dependencies> </project>上面代碼有時(shí)候取不到本地連接地址,這個(gè)時(shí)候需要嘗試讓agent加載management-agent.jar,完整代碼如下:
?
package com.dxz.study;import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.List; import java.util.Properties;public class AbstractJmxCommand {private static final String CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress";public static String getJVM() {return System.getProperty("java.vm.specification.vendor");}public static boolean isSunJVM() {// need to check for Oracle as that is the name for Java7 onwards.return getJVM().equals("Sun Microsystems Inc.") || getJVM().startsWith("Oracle");}public static void main(String[] args) {if (args == null || args.length == 0) {System.out.println("Usage: pid");return;}int pid = Integer.valueOf(args[0]);System.out.println(new AbstractJmxCommand().findJMXUrlByProcessId(pid));}/*** Finds the JMX Url for a VM by its process id* * @param pid* The process id value of the VM to search for.* * @return the JMX Url of the VM with the given pid or null if not found.*/// @SuppressWarnings({ "rawtypes", "unchecked" })protected String findJMXUrlByProcessId(int pid) {if (isSunJVM()) {try {// Classes are all dynamically loaded, since they are specific// to Sun VM// if it fails for any reason default jmx url will be used// tools.jar are not always included used by default class// loader, so we// will try to use custom loader that will try to load tools.jar String javaHome = System.getProperty("java.home");String tools = javaHome + File.separator + ".." + File.separator + "lib" + File.separator + "tools.jar";URLClassLoader loader = new URLClassLoader(new URL[] { new File(tools).toURI().toURL() });Class virtualMachine = Class.forName("com.sun.tools.attach.VirtualMachine", true, loader);Class virtualMachineDescriptor = Class.forName("com.sun.tools.attach.VirtualMachineDescriptor", true,loader);Method getVMList = virtualMachine.getMethod("list", (Class[]) null);Method attachToVM = virtualMachine.getMethod("attach", String.class);Method getAgentProperties = virtualMachine.getMethod("getAgentProperties", (Class[]) null);Method getVMId = virtualMachineDescriptor.getMethod("id", (Class[]) null);List allVMs = (List) getVMList.invoke(null, (Object[]) null);for (Object vmInstance : allVMs) {String id = (String) getVMId.invoke(vmInstance, (Object[]) null);if (id.equals(Integer.toString(pid))) {Object vm = attachToVM.invoke(null, id);Properties agentProperties = (Properties) getAgentProperties.invoke(vm, (Object[]) null);String connectorAddress = agentProperties.getProperty(CONNECTOR_ADDRESS);if (connectorAddress != null) {return connectorAddress;} else {break;}}}// 上面的嘗試都不成功,則嘗試讓agent加載management-agent.jarMethod getSystemProperties = virtualMachine.getMethod("getSystemProperties", (Class[]) null);Method loadAgent = virtualMachine.getMethod("loadAgent", String.class, String.class);Method detach = virtualMachine.getMethod("detach", (Class[]) null);for (Object vmInstance : allVMs) {String id = (String) getVMId.invoke(vmInstance, (Object[]) null);if (id.equals(Integer.toString(pid))) {Object vm = attachToVM.invoke(null, id);Properties systemProperties = (Properties) getSystemProperties.invoke(vm, (Object[]) null);String home = systemProperties.getProperty("java.home");// Normally in ${java.home}/jre/lib/management-agent.jar// but might// be in ${java.home}/lib in build environments. String agent = home + File.separator + "jre" + File.separator + "lib" + File.separator+ "management-agent.jar";File f = new File(agent);if (!f.exists()) {agent = home + File.separator + "lib" + File.separator + "management-agent.jar";f = new File(agent);if (!f.exists()) {throw new IOException("Management agent not found");}}agent = f.getCanonicalPath();loadAgent.invoke(vm, agent, "com.sun.management.jmxremote");Properties agentProperties = (Properties) getAgentProperties.invoke(vm, (Object[]) null);String connectorAddress = agentProperties.getProperty(CONNECTOR_ADDRESS);// detach 這個(gè)vmdetach.invoke(vm, (Object[]) null);if (connectorAddress != null) {return connectorAddress;} else {break;}}}} catch (Exception ignore) {ignore.printStackTrace();}}return null;} }?
?
2、遠(yuǎn)程連接
毫無疑問,若想遠(yuǎn)程連接訪問,肯定需要mBeanServer注冊(cè)一個(gè)或多個(gè)端口,如rmi端口,http端口等。2.1 rmi端口注冊(cè)及訪問
有兩種方法,一種直接在代碼里面指定rmi端口,并綁定,如下,此種方法需要使用客戶端連接代碼訪問,另一種代碼不用指定端口,之需把mbean注冊(cè)到platformMBeanServer 里面,并在啟動(dòng)進(jìn)程時(shí)加jmx參數(shù)指定,用這種方法可以通過jconsole,jvisualvm遠(yuǎn)程訪問。2.1.1 直接在代碼里面綁定端口
@Test public void testJmxRmiRegist() throws Exception { int rmiPort = 2222; String jmxServerName = "com.dxz.study.TestJmxRmiRegist"; // jdkfolder/bin/rmiregistry.exe 9999 Registry registry = LocateRegistry.createRegistry(rmiPort); MBeanServer mbs = MBeanServerFactory.createMBeanServer(jmxServerName); System.out.println(mbs); // mbs = MBeanServerFactory.createMBeanServer(); // 新建MBean ObjectName, 在MBeanServer里標(biāo)識(shí)注冊(cè)的MBean ObjectName name = new ObjectName(jmxServerName + ":type=HelloWorld"); // HtmlAdaptorServer adapter = new HtmlAdaptorServer(); // 在MBeanServer里注冊(cè)MBean, 標(biāo)識(shí)為ObjectName(com.tenpay.jmx:type=Echo) mbs.registerMBean(new HelloWorld(), name); JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + rmiPort + "/" + jmxServerName); System.out.println("JMXServiceURL: " + url.toString()); JMXConnectorServer jmxConnServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); jmxConnServer.start(); Thread.sleep(1000 * 60 * 10); } 上面程序是新建了個(gè)mbeanserver,并通過rmi綁定到2222端口上,等待客戶端連接。2.1.2 通過jmx參數(shù)啟動(dòng)進(jìn)程
#JVMARGS="$JVMARGS -Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" 通過這種把進(jìn)程的jmx監(jiān)控綁定指定的端口,即可在遠(yuǎn)端通過jconsole進(jìn)行監(jiān)控。2.2通過http訪問
3、客戶端連接
package com.dxz.study;import java.util.Set;import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL;import org.junit.Test;public class JmxClientTest {@Test public void test1() { try { JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:2222/com.dxz.study.TestJmxRmiRegist"); JMXConnector connector = JMXConnectorFactory.connect(url); MBeanServerConnection mbeanConn = connector.getMBeanServerConnection(); Set<ObjectName> beanSet = mbeanConn.queryNames(null, null); System.out.println(beanSet); }catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }結(jié)果:
[com.dxz.study.TestJmxRmiRegist:type=HelloWorld, JMImplementation:type=MBeanServerDelegate]
4、jconsole連接(待驗(yàn)證)
5、java進(jìn)程自帶的mbean
當(dāng)我們?cè)谟胘console、jvisualvm進(jìn)行監(jiān)控java進(jìn)程時(shí),通常都能看到cpu、內(nèi)存、線程、垃圾收集等使用情況,其實(shí)數(shù)據(jù)都是通過jmx從jvm提供的一些mbean里面取的。主要如下:- ClassLoadingMXBean
ClassLoadMXBean 包括一些類的裝載信息,比如有多少類已經(jīng)裝載 / 卸載(unloaded),虛擬機(jī)類裝載的 verbose 選項(xiàng)(即命令行中的 Java – verbose:class 選項(xiàng))是否打開,還可以幫助用戶打開 / 關(guān)閉該選項(xiàng)。
- CompilationMXBean
CompilationMXBean 幫助用戶了解當(dāng)前的編譯器和編譯情況,該 mxbean 提供的信息不多。
- GarbageCollectorMXBean
相對(duì)于開放人員對(duì) GC 的關(guān)注程度來說,該 mxbean 提供的信息十分有限,僅僅提供了 GC 的次數(shù)和 GC 花費(fèi)總時(shí)間的近似值。但是這個(gè)包中還提供了三個(gè)的內(nèi)存管理檢測(cè)類:MemoryManagerMXBean,MemoryMXBean 和 MemoryPoolMXBean。
- MemoryManagerMXBean
這個(gè)類相對(duì)簡(jiǎn)單,提供了內(nèi)存管理類和內(nèi)存池(memory pool)的名字信息。
- MemoryMXBean
這個(gè)類提供了整個(gè)虛擬機(jī)中內(nèi)存的使用情況,包括 Java 堆(heap)和非 Java 堆所占用的內(nèi)存,提供當(dāng)前等待 finalize 的對(duì)象數(shù)量,它甚至可以做 gc(實(shí)際上是調(diào)用 System.gc)。
- MemoryPoolMXBean
該信息提供了大量的信息。在 JVM 中,可能有幾個(gè)內(nèi)存池,因此有對(duì)應(yīng)的內(nèi)存池信息,因此,在工廠類中,getMemoryPoolMXBean() 得到是一個(gè) MemoryPoolMXBean 的 list。每一個(gè) MemoryPoolMXBean 都包含了該內(nèi)存池的詳細(xì)信息,如是否可用、當(dāng)前已使用內(nèi)存 / 最大使用內(nèi)存值、以及設(shè)置最大內(nèi)存值等等。
- MemoryManagerMXBean
- OperatingSystemMXBean
該類提供的是操作系統(tǒng)的簡(jiǎn)單信息,如構(gòu)架名稱、當(dāng)前 CPU 數(shù)、最近系統(tǒng)負(fù)載等。
- RuntimeMXBean
運(yùn)行時(shí)信息包括當(dāng)前虛擬機(jī)的名稱、提供商、版本號(hào),以及 classpath、bootclasspath 和系統(tǒng)參數(shù)等等。
- ThreadMXBean
在 Java 這個(gè)多線程的系統(tǒng)中,對(duì)線程的監(jiān)控是相當(dāng)重要的。ThreadMXBean 就是起到這個(gè)作用。ThreadMXBean 可以提供的信息包括各個(gè)線程的各種狀態(tài),CPU 占用情況,以及整個(gè)系統(tǒng)中的線程狀況。從 ThreadMXBean 可以得到某一個(gè)線程的 ThreadInfo 對(duì)象。這個(gè)對(duì)象中則包含了這個(gè)線程的所有信息。
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/duanxz/p/4474750.html
總結(jié)
以上是生活随笔為你收集整理的JMX-JAVA进程监控利器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU 5214 Movie【贪心】
- 下一篇: iOS开发之 几本书