JMX-JAVA进程监控利器

时间:2022-09-02 22:52:48

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。

JMX-JAVA进程监控利器

注:

  1. 一个java进程里面可以有多个不同名字的mBeanServer ,每个mbs都是一个独立的容器,用了管理mbean
  2. 每个mbs都可以注册多个rmi port,http port等
  3. platformMBeanServer 是由jvm创建的,并添加了一些系统的mbean,如cpu,内存,网络,线程等等

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.jar
Method 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 这个vm
detach.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访问

  1. @Test
    public void testJmxHtmlAdapter() throws Exception {
    String jmxServerName = "com.dxz.study.TestJmxRmiRegist"; // jdkfolder/bin/rmiregistry.exe 9999 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();
    // 创建MBean
    // 在MBeanServer里注册MBean, 标识为ObjectName(com.tenpay.jmx:type=Echo)
    mbs.registerMBean(new HelloWorld(), name);
    HtmlAdaptorServer adapter = new HtmlAdaptorServer();
    ObjectName adapterName;
    adapterName = new ObjectName(jmxServerName + ":name=" + "htmladapter");
    adapter.setPort(8082);
    adapter.start();
    mbs.registerMBean(adapter, adapterName); Thread.sleep(1000 * 60 * 10);
    }

以上代码用到了HtmlAdaptorServer,

<dependency>
<groupId>org.glassfish.external</groupId>
<artifactId>opendmk_jdmkrt_jar</artifactId>
<version>1.0-b01-ea</version>
</dependency>
然后用浏览器访问即可
 
JMX-JAVA进程监控利器

3、客户端连接

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 都包含了该内存池的详细信息,如是否可用、当前已使用内存 / 最大使用内存值、以及设置最大内存值等等。

  • OperatingSystemMXBean

    该类提供的是操作系统的简单信息,如构架名称、当前 CPU 数、最近系统负载等。

  • RuntimeMXBean

    运行时信息包括当前虚拟机的名称、提供商、版本号,以及 classpath、bootclasspath 和系统参数等等。

  • ThreadMXBean

    在 Java 这个多线程的系统中,对线程的监控是相当重要的。ThreadMXBean 就是起到这个作用。ThreadMXBean 可以提供的信息包括各个线程的各种状态,CPU 占用情况,以及整个系统中的线程状况。从 ThreadMXBean 可以得到某一个线程的 ThreadInfo 对象。这个对象中则包含了这个线程的所有信息。

要获得这些信息,我们首先通过 java.lang.management.ManagementFactory这个工厂类来获得一系列的 MXBean。
ClassLoadingMXBean mbs = ManagementFactory.getClassLoadingMXBean();
System.out.println("loadedClass:" + mbs.getLoadedClassCount());

JMX-JAVA进程监控利器的更多相关文章

  1. 老技术新谈,Java应用监控利器JMX(2)

    各位坐稳扶好,我们要开车了.不过在开车之前,我们还是例行回顾一下上期分享的要点. 上期由于架不住来自于程序员内心的灵魂的拷问,于是我们潜心修炼,与 Java 应用监控利器 JMX 正式打了个照面. J ...

  2. 轻量级监控平台之java进程监控脚本

    轻量级监控平台之java进程监控脚本 #!/bin/bash #进程监控脚本 #功能需求: 上报机器Java进程的进程ID,对应的端口号service tcp端口号,tomcat http 端口号,以 ...

  3. Java进程监控

    目录 1.引言 2. 程序启停, 为进程自定义项目名称 3. 操作系统判断 4. 获取进程信息 5. 内存,CPU信息 6. 堆内存信息 7. 端口信息 8. 线程信息 9. MXBean使用样例 9 ...

  4. 初识阿里开源的本地Java进程监控调试工具arthas(阿尔萨斯)

    转载自:https://www.cnblogs.com/linhui0705/p/9795417.html 上个月,阿里开源了一个名为Arthas的监控工具.恰逢近期自己在写多线程处理业务,由此想到了 ...

  5. 老技术新谈,Java应用监控利器JMX(3)

    各位坐稳扶好,我们要开车了.不过在开车之前,我们还是例行回顾一下上期分享的要点. 上期我们深入的聊了聊 JMX,把 JMX 的架构了解了七七八八,最后通过代码实战,解决系列疑问,实现远程动态修改应用参 ...

  6. 老技术新谈,Java应用监控利器JMX(1)

    先聊聊最近比较流行的梗,来一次灵魂八问. 配钥匙师傅: 你配吗? 食堂阿姨: 你要饭吗? 算命先生: 你算什么东西? 快递小哥: 你是什么东西? 上海垃圾分拣阿姨: 你是什么垃圾? 滴滴司机: 你搞清 ...

  7. Java应用监控利器JMX

    啥是 JMX? The Java Management Extensions (JMX) API is a standard API for management and monitoring of ...

  8. Linux记录-JMX监控JAVA进程

    3.修改xxx.sh 加入export JAVA_OPTS="-Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.mana ...

  9. 利用VisualVm和JMX远程监控Java进程

    自Java 6开始,Java程序启动时都会在JVM内部启动一个JMX agent,JMX agent会启动一个MBean server组件,把MBeans(Java平台标准的MBean + 你自己创建 ...

随机推荐

  1. 【原创】开源Math&period;NET基础数学类库使用&lpar;06&rpar;直接求解线性方程组

                   本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新  开源Math.NET基础数学类库使用总目录:[目录]开源Math.NET基础数学类库使用总目录 前言 ...

  2. Mac Pro 开机自启动 PHP-FPM,Nginx,MySql 等软件

    在Mac下安装好了PHP开发环境(PHP-FPM,Nginx,MySql), 想设置成开机自启动,原来以为和一般的Linux系统一样,也是在rc.d这样目录放置启动脚本.在网上查了一些资料,发现苹果应 ...

  3. compareTo &amp&semi; toString

    public class UnAssignedRecord implements Comparable<UnAssignedRecord> { private String time; / ...

  4. win向linux传文件

    使用pscp.exe即可. 下载pscp.exe(http://pan.baidu.com/s/1jG6zmx4) 复制到windows/system32目录下即可. 然后可在cmd命令行下使用psc ...

  5. freemarker书写select组件错误摘要(七)

    1.错误叙述性说明 六月 26, 2014 11:26:27 下午 freemarker.log.JDK14LoggerFactory$JDK14Logger error 严重: Template p ...

  6. kali使用

    1.kali安装后安装vmtools ①.vim /etc/apt/sources.list 添加中科大滚动版更新源 deb http://mirrors.ustc.edu.cn/kali kali- ...

  7. extjs&period;net 按钮执行并显示Mask代码

    <ext:Button ID="ButtonTest" runat="server"  Width="65" Text="同 ...

  8. UOJ&num;23&period; 【UR &num;1】跳蚤国王下江南 仙人掌 Tarjan 点双 圆方树 点分治 多项式 FFT

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ23.html 题目传送门 - UOJ#23 题意 给定一个有 n 个节点的仙人掌(可能有重边). 对于所有 ...

  9. 《LINUX内核设计与实现》第三周读书笔记——第一二章

    <Linux内核设计与实现>读书笔记--第一二章 20135301张忻 估算学习时间:共2小时 读书:1.5 代码:0 作业:0 博客:0.5 实际学习时间:共2.5小时 读书:2.0 代 ...

  10. js-权威指南学习笔记15&period;3

    1.如果要明确地选取一个表单元素,可以索引表单对象的elements属性:document.forms.address.elements.street--一个name为address的form下的na ...