JMX-JAVA进程监控利器
Java?管理擴展(Java Management Extension,JMX)是從jdk1.4開始的,但從1.5時才加到jdk里面,并把API放到java.lang.management包里面。
如果一個 Java 對象可以由一個遵循 JMX 規范的管理器應用管理,那么這個Java 對象就可以稱為一個可由 JMX 管理的資源。
要使一個 Java 對象可管理,則必須創建相應的 MBean 對象,并通過這些 MBean 對象管理相應的 Java 對象。當擁有 MBean 類后,需要將其實例化并注冊到 MBeanServer 上。
一共有四種類型的 MBean , 分別是標準類型 MBean, 動態類型 MBean, 開放類型 MBean 和模型類型 MBean。
?
注:
?
1、本機使用
當我們啟動java進程后,經常會使用jps,jinfo,jmap,jstat等jdk自帶的命令去查詢進程的狀態,這其中的原理就是,當java進程啟動后,會創建一個用于本機連接的“localConnectorAddress”放到當前用戶目錄下,當使用jps等連接時,會到當前用戶目錄下取到“localConnectorAddress”并連接。
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("進程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,本地或者遠程 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>上面代碼有時候取不到本地連接地址,這個時候需要嘗試讓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 這個vmdetach.invoke(vm, (Object[]) null);if (connectorAddress != null) {return connectorAddress;} else {break;}}}} catch (Exception ignore) {ignore.printStackTrace();}}return null;} }?
?
2、遠程連接
毫無疑問,若想遠程連接訪問,肯定需要mBeanServer注冊一個或多個端口,如rmi端口,http端口等。2.1 rmi端口注冊及訪問
有兩種方法,一種直接在代碼里面指定rmi端口,并綁定,如下,此種方法需要使用客戶端連接代碼訪問,另一種代碼不用指定端口,之需把mbean注冊到platformMBeanServer 里面,并在啟動進程時加jmx參數指定,用這種方法可以通過jconsole,jvisualvm遠程訪問。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里標識注冊的MBean ObjectName name = new ObjectName(jmxServerName + ":type=HelloWorld"); // HtmlAdaptorServer adapter = new HtmlAdaptorServer(); // 在MBeanServer里注冊MBean, 標識為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); } 上面程序是新建了個mbeanserver,并通過rmi綁定到2222端口上,等待客戶端連接。2.1.2 通過jmx參數啟動進程
#JVMARGS="$JVMARGS -Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" 通過這種把進程的jmx監控綁定指定的端口,即可在遠端通過jconsole進行監控。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(); } } }結果:
[com.dxz.study.TestJmxRmiRegist:type=HelloWorld, JMImplementation:type=MBeanServerDelegate]
4、jconsole連接(待驗證)
5、java進程自帶的mbean
當我們在用jconsole、jvisualvm進行監控java進程時,通常都能看到cpu、內存、線程、垃圾收集等使用情況,其實數據都是通過jmx從jvm提供的一些mbean里面取的。主要如下:- ClassLoadingMXBean
ClassLoadMXBean 包括一些類的裝載信息,比如有多少類已經裝載 / 卸載(unloaded),虛擬機類裝載的 verbose 選項(即命令行中的 Java – verbose:class 選項)是否打開,還可以幫助用戶打開 / 關閉該選項。
- CompilationMXBean
CompilationMXBean 幫助用戶了解當前的編譯器和編譯情況,該 mxbean 提供的信息不多。
- GarbageCollectorMXBean
相對于開放人員對 GC 的關注程度來說,該 mxbean 提供的信息十分有限,僅僅提供了 GC 的次數和 GC 花費總時間的近似值。但是這個包中還提供了三個的內存管理檢測類:MemoryManagerMXBean,MemoryMXBean 和 MemoryPoolMXBean。
- MemoryManagerMXBean
這個類相對簡單,提供了內存管理類和內存池(memory pool)的名字信息。
- MemoryMXBean
這個類提供了整個虛擬機中內存的使用情況,包括 Java 堆(heap)和非 Java 堆所占用的內存,提供當前等待 finalize 的對象數量,它甚至可以做 gc(實際上是調用 System.gc)。
- MemoryPoolMXBean
該信息提供了大量的信息。在 JVM 中,可能有幾個內存池,因此有對應的內存池信息,因此,在工廠類中,getMemoryPoolMXBean() 得到是一個 MemoryPoolMXBean 的 list。每一個 MemoryPoolMXBean 都包含了該內存池的詳細信息,如是否可用、當前已使用內存 / 最大使用內存值、以及設置最大內存值等等。
- MemoryManagerMXBean
- OperatingSystemMXBean
該類提供的是操作系統的簡單信息,如構架名稱、當前 CPU 數、最近系統負載等。
- RuntimeMXBean
運行時信息包括當前虛擬機的名稱、提供商、版本號,以及 classpath、bootclasspath 和系統參數等等。
- ThreadMXBean
在 Java 這個多線程的系統中,對線程的監控是相當重要的。ThreadMXBean 就是起到這個作用。ThreadMXBean 可以提供的信息包括各個線程的各種狀態,CPU 占用情況,以及整個系統中的線程狀況。從 ThreadMXBean 可以得到某一個線程的 ThreadInfo 對象。這個對象中則包含了這個線程的所有信息。
?
?
轉載于:https://www.cnblogs.com/duanxz/p/4474750.html
總結
以上是生活随笔為你收集整理的JMX-JAVA进程监控利器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU 5214 Movie【贪心】
- 下一篇: iOS开发之 几本书