利用jdk中工具完成Java程序监控方法记录


转载加自己整理的部分内容,转载自:http://jiajun.iteye.com/blog/810150

记录下JConsole使用方法

 一、JConsole是什么
    从Java 5开始 引入了 JConsole。JConsole 是一个内置 Java 性能分析器,可以从命令行或在 GUI shell 中运行。您可以轻松地使用 JConsole(或者,它更高端的 “近亲” VisualVM )来监控 Java 应用程序性能和跟踪 Java 中的代码。
二、如何启动JConsole
1.    如果是从命令行启动,使 JDK 在 PATH 上,运行 jconsole 即可。
2.    如果从 GUI shell 启动,找到 JDK 安装路径,打开 bin 文件夹,双击 jconsole 。
    当分析工具弹出时(取决于正在运行的 Java 版本以及正在运行的 Java 程序数量),可能会出现一个对话框,要求输入一个进程的 URL 来连接,也可能列出许多不同的本地 Java 进程(有时包含 JConsole 进程本身)来连接。如图所示:
 
想分析那个程序就双击那个进程。
三、如何设置JAVA程序运行时可以被JConsolse连接分析
1.    本地程序(相对于开启JConsole的计算机),无需设置任何参数就可以被本地开启的JConsole连接(Java SE 6开始无需设置,之前还是需要设置运行时参数 -Dcom.sun.management.jmxremote )
2.    无认证连接 (下面的设置表示:连接的端口为8999、无需认证就可以被连接)

如下这些参数需要设置为虚拟机运行参数不是程序运行参数
1).    -Dcom.sun.management.jmxremote
2).    -Dcom.sun.management.jmxremote.port=8999 \  
3).    -Dcom.sun.management.jmxremote.authenticate=false \  
4).    -Dcom.sun.management.jmxremote.ssl=false  


3.    如果考虑到安全因素,需要认证,需要安全连接,也是可以搞定的。参考:http://download.oracle.com/javase/6/docs/technotes/guides/management/agent.html#gdenv
四、JConsole如何连接远程机器的JAVA程序(举例说明)
1、写一个简单的一直运行的JAVA程序,运行在某台机器上如(192.168.0.181)
1).    java -cp . -Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.managent.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false JConsoleTest  
 
2、另外一台机器进行连接
可以直接使用命令:
1).    jconsole.exe 192.168.0.181:8999  
 也可以在已经打开的JConsole界面操作 连接->新建连接->选择远程进程->输入远程主机IP和端口号->点击“连接”,如图:
 
 然后就会进入分析界面:
性能分析
下面说说如何分析,如何使用这六个标签
•    概述: Displays overview information about the Java VM and monitored values.
•    内存: 显示内存使用信息
•    线程: 显示线程使用信息
•    类: 显示类装载信息
•    *VM摘要:*显示java VM信息
•    MBeans: 显示 MBeans.
概述
 
 
    概述很简单没啥说的,自己看看吧,不过值得一提的是对着图点击右键可以保存数据到CSV文件,以后可以使用其他工具来分析这些数据。
内存
 
    这个比较有价值,参看堆内存,非堆内存,内存池的状况总体内存的分配和使用情况以及不同的GC进行垃圾回收的次数和时间。可以手动进行GC查看内存变化。
 
   在分析JAVA内存问题进行调优时候非常有用,你要学习JVM内存模型,之后会发现这里的每个值都具有意义。
 
   GC的算法和参数对性能有显著的影响,注意垃圾回收次数、时间、以及partial GC和full GC,调整你所使用的不同GC和以及各个GC下的参数,然后在这个视图下观察,以得到好的性能。
 
这里贴一下 Java HotSpot VM garbage collector 下generational GC 的各代的划分图:
 
关于GC,可以参考:http://www.oracle.com/technetwork/java/gc-tuning-5-138395.html
线程
 
    左下角显示所有的活动线程(如果线程过多,可以在下面的过滤栏中输入字符串过滤出你想要观察的线程)。点击某个显示会显示这个线程的名称、状态、阻塞和等待的次数、堆栈的信息。
 
    统计图显示的是线程数目的峰值(红色)和当前活动的线程(蓝色)。
 
   另外下面有个按钮“检测到死锁”,有时候会有用处。

 
没啥要说的。
VM摘要
 
也没啥要说的,看看吧,内存状况,操作系统...
Mbean
一个MBean是一个被管理的Java对象,有点类似于JavaBean,一个设备、一个应用或者任何资源都可以被表示为MBean,MBean会暴露一个接口对外,这个接口可以读取或者写入一些对象中的属性。Mbean实现类需要注册如相应MbeanServer中,才可以在JConsole工具中查看相应接口信息。
 
这里可以有一些额外的操作。
使用插件
1).    jconsole -pluginpath C:\Java\jdk1.6.0_22\demo\management\JTop\JTop.jar  
 

 

 

JMX及MBean讲解与使用,

转载自:https://www.cnblogs.com/dongguacai/p/5900507.html

 

一、JMX的定义  

  JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,实际上,用户可以在任何Java应用程序中使用这些代理和服 务实现管理。这是官方文档上的定义,我看过很多次也无法很好的理解。我个人的理解是JMX让程序有被管理的功能,例如你开发一个WEB网站,它是在24小 时不间断运行,那么你肯定会对网站进行监控,如每天的UV、PV是多少;又或者在业务高峰的期间,你想对接口进行限流,就必须去修改接口并发的配置值。

  应用场景:中间件软件WebLogic的管理页面就是基于JMX开发的,而JBoss则整个系统都基于JMX构架。

对于一些参数的修改,网上有一段描述还是比较形象的:

1、程序初哥一般是写死在程序中,到要改变的时候就去修改代码,然后重新编译发布。

2、程序熟手则配置在文件中(JAVA一般都是properties文件),到要改变的时候只要修改配置文件,但还是必须重启系统,以便读取配置文件里最新的值。

3、程序好手则会写一段代码,把配置值缓存起来,系统在获取的时候,先看看配置文件有没有改动,如有改动则重新从配置里读取,否则从缓存里读取。

4、程序高手则懂得物为我所用,用JMX把需要配置的属性集中在一个类中,然后写一个MBean,再进行相关配置。另外JMX还提供了一个工具页,以方便我们对参数值进行修改。

二、JMX架构图:

从图中我们可以看到,JMX的结构一共分为三层:

1、基础层:主要是MBean,被管理的资源。

MBean分为如下四种,我接下来主要介绍standard MBean

类型 描述
standard MBean 这种类型的MBean最简单,它能管理的资源(包括属性,方法,时间)必须定义在接口中,然后MBean必须实现这个接口。它的命名也必须遵循一定的规范,例如我们的MBean为Hello,则接口必须为HelloMBean。
dynamic MBean 必须实现javax.management.DynamicMBean接口,所有的属性,方法都在运行时定义
open MBean 此MBean的规范还不完善,正在改进中
model MBean 与标准和动态MBean相比,你可以不用写MBean类,只需使用 javax.management.modelmbean.RequiredModelMBean即可。RequiredModelMBean实现了 ModelMBean接口,而ModelMBean扩展了DynamicMBean接口,因此与DynamicMBean相似,Model MBean的管理资源也是在运行时定义的。与DynamicMBean不同的是,DynamicMBean管理的资源一般定义在DynamicMBean 中(运行时才决定管理那些资源),而model MBean管理的资源并不在MBean中,而是在外部(通常是一个类),只有在运行时,才通过set方法将其加入到model MBean中。后面的例子会有详细介绍

2、适配层:MBeanServer,主要是提供对资源的注册和管理。

3、接入层:提供远程访问的入口。

 

接下来我这里会用程序来介绍三种访问JMX的方式:

三、JDK的小工具Jconsole访问

 1、 首先定义一个MBean接口,接口的命名规范为以具体的实现类为前缀(这个规范很重要)

复制代码
 1 package jmx;  2  3 public interface HelloMBean  4 {  5 public String getName();  6  7 public void setName(String name);  8  9 public String getAge(); 10 11 public void setAge(String age); 12 13 public void helloWorld(); 14 15 public void helloWorld(String str); 16 17 public void getTelephone(); 18 }
复制代码

2、定义一个实现类,实现上面的接口:

复制代码
 1 package jmx;  2  3 /*  4  * 该类名称必须与实现的接口的前缀保持一致(即MBean前面的名称  5 */  6 public class Hello implements HelloMBean  7 {  8 private String name;  9 10 private String age; 11 12 public void getTelephone() 13  { 14 System.out.println("get Telephone"); 15  } 16 17 public void helloWorld() 18  { 19 System.out.println("hello world"); 20  } 21 22 public void helloWorld(String str) 23  { 24 System.out.println("helloWorld:" + str); 25  } 26 27 public String getName() 28  { 29 System.out.println("get name 123"); 30 return name; 31  } 32 33 public void setName(String name) 34  { 35 System.out.println("set name 123"); 36 this.name = name; 37  } 38 39 public String getAge() 40  { 41 System.out.println("get age 123"); 42 return age; 43  } 44 45 public void setAge(String age) 46  { 47 System.out.println("set age 123"); 48 this.age = age; 49  } 53 }
复制代码

3、定义agent层:

复制代码
 1 package jmx;  2  3 import java.lang.management.ManagementFactory;  4  5 import javax.management.JMException;  6 import javax.management.MBeanServer;  7 import javax.management.ObjectName;  8  9 public class HelloAgent 10 { 11 public static void main(String[] args) throws JMException, Exception 12  { 13 MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 14 ObjectName helloName = new ObjectName("jmxBean:name=hello"); 15 //create mbean and register mbean 16 server.registerMBean(new Hello(), helloName); 17 Thread.sleep(60*60*1000); 18  } 19 }
复制代码

1、其中第13行是通过工厂类获取MBeanServer,用来做MBean的容器 。

2、第14行中的ObjectName中的取名是有一定规范的,格式为:“域名:name=MBean名称”,其中域名和MBean的名称可以任意取。这样定义后,就可以唯一标识我们定义的这个MBean的实现类了。

3、第16行是将Hello这个类注入到MBeanServer中,注入需要创建一个ObjectName类 

这样,一个简单的JMX的DEMO已经写完了,现在我们通过JDK提供的Jconsole来进行操作。

1、首先在自己的本地路径下:C:\Program Files (x86)\Java\jdk1.6.0_43\bin找到jconsole.exe这个小工具,双击打开:

2、双击打开我们的本地进程:HelloAgent:

3.在这个界面上,我们可以给程序中HelloMBean的属性赋值,也可以调用其中的方法:

4、控制台打印如下:

四、通过JMX提供的工具页访问

这里,我们复用上面的接口和实现类,只需要改动适配层,这里需要到导入外部jar包jdmk

复制代码
package jmx; import java.lang.management.ManagementFactory; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; import com.sun.jdmk.comm.HtmlAdaptorServer; public class HelloAgent { public static void main(String[] args) throws JMException, Exception { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); ObjectName helloName = new ObjectName("jmxBean:name=hello"); //create mbean and register mbean server.registerMBean(new Hello(), helloName); ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8082"); HtmlAdaptorServer adapter = new HtmlAdaptorServer(); server.registerMBean(adapter, adapterName); adapter.start(); } }
复制代码

我们访问地址:http://localhost:8082,点击name=hello:

1、在这里创建一个AdaptorServer,这个类将决定MBean的管理界面,这里用最普通的Html型界面。AdaptorServer其实也是一个MBean。 

2、我们可以看到这个工具页,其实与我们上一个案例中的Jconsole中的管理界面类似,都可以操作资源中的属性和方法。

五、通过客户端程序进行远程访问

 1、这里需要对agent进行修改,增加ip和porta绑定部分的逻辑

复制代码
package jmxTest; import java.io.IOException; import java.lang.management.ManagementFactory; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; public class HelloAgent { public static void main(String[] args) throws JMException, NullPointerException { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); ObjectName helloName = new ObjectName("jmxBean:name=hello"); //create mbean and register mbean server.registerMBean(new Hello(), helloName); try { //这个步骤很重要,注册一个端口,绑定url后用于客户端通过rmi方式连接JMXConnectorServer LocateRegistry.createRegistry(9999); //URL路径的结尾可以随意指定,但如果需要用Jconsole来进行连接,则必须使用jmxrmi JMXServiceURL url = new JMXServiceURL ("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi"); JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server); System.out.println("begin rmi start"); jcs.start(); System.out.println("rmi start"); } catch (RemoteException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } 
}
}
复制代码

  写到这里,如果没有client进行远程连接,可以使用Jconsole进行远程访问:

2、客户端Client程序,用于与agent进行远程连接:

复制代码
 1 package jmx;  2  3 import java.io.IOException;  4  5 import javax.management.Attribute;  6 import javax.management.MBeanServerConnection;  7 import javax.management.MBeanServerInvocationHandler;  8 import javax.management.ObjectName;  9 import javax.management.remote.JMXConnector; 10 import javax.management.remote.JMXConnectorFactory; 11 import javax.management.remote.JMXServiceURL; 12 13 14 public class Client 15 { 16 public static void main(String[] args) throws IOException, Exception, NullPointerException 17  { 18 JMXServiceURL url = new JMXServiceURL 19 ("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi"); 20 JMXConnector jmxc = JMXConnectorFactory.connect(url,null); 21 22 MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 23 //ObjectName的名称与前面注册时候的保持一致 24 ObjectName mbeanName = new ObjectName("jmxBean:name=hello"); 25 26 System.out.println("Domains ......"); 27 String[] domains = mbsc.getDomains(); 28 29 for(int i=0;i<domains.length;i++) 30  { 31 System.out.println("doumain[" + i + "]=" + domains[i] ); 32  } 33 34 System.out.println("MBean count = " + mbsc.getMBeanCount()); 35 //设置指定Mbean的特定属性值 36 //这里的setAttribute、getAttribute操作只能针对bean的属性 37 //例如对getName或者setName进行操作,只能使用Name,需要去除方法的前缀 38 mbsc.setAttribute(mbeanName, new Attribute("Name","杭州")); 39 mbsc.setAttribute(mbeanName, new Attribute("Age","1990")); 40 String age = (String)mbsc.getAttribute(mbeanName, "Age"); 41 String name = (String)mbsc.getAttribute(mbeanName, "Name"); 42 System.out.println("age=" + age + ";name=" + name); 43 44 HelloMBean proxy = MBeanServerInvocationHandler. 45 newProxyInstance(mbsc, mbeanName, HelloMBean.class, false); 46  proxy.helloWorld(); 47 proxy.helloWorld("migu"); 48  proxy.getTelephone(); 49 //invoke调用bean的方法,只针对非设置属性的方法 50 //例如invoke不能对getName方法进行调用 51 mbsc.invoke(mbeanName, "getTelephone", null, null); 52 mbsc.invoke(mbeanName, "helloWorld", 53 new String[]{"I'll connect to JMX Server via client2"}, new String[]{"java.lang.String"}); 54 mbsc.invoke(mbeanName, "helloWorld", null, null); 55  } 56 }
复制代码

a、在35到41行,是对属性进行赋值和取值,这里我们不能直接调用方法,而是通过setAttribute、getAttrubute方法来进行操作,则属性的首字母要大写。

b、对资源里面的方法进行操作有两种方式:一是通过代理直接调用方法;二是通过JAVA的反射注入的方式进行方法的调用。

下面我们来看看执行结果,先执行agent,再执行客户端:

c、client的控制台打印结果:

 

d、agent控制台打印结果:

 六、Notification

 MBean之间的通信是必不可少的,Notification就起到了在MBean之间沟通桥梁的作用。JMX 的通知由四部分组成:

1、Notification这个相当于一个信息包,封装了需要传递的信息

2、Notification broadcaster这个相当于一个广播器,把消息广播出。

3、Notification listener 这是一个监听器,用于监听广播出来的通知信息。

4、Notification filiter 这个一个过滤器,过滤掉不需要的通知。这个一般很少使用。

这里我们使用日常打招呼的场景:jack与我偶遇,jack说:hi;我礼貌的回答:hello,jack。

这里我们先分别创建两个资源:

复制代码
package jmx; /* * 该类名称必须与实现的接口的前缀保持一致(即MBean前面的名称 */ public class Hello implements HelloMBean { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void printHello() { System.out.println("Hello World, " + name); } public void printHello(String whoName) { System.out.println("Hello , " + whoName); } }
复制代码
复制代码
package jmx; /* * 接口名必须以MBean结尾 */ public interface HelloMBean { public String getName(); public void setName(String name); public void printHello(); public void printHello(String whoName); }
复制代码
复制代码
package jmx; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; public class Jack extends NotificationBroadcasterSupport implements JackMBean { private int seq = 0; public void hi() { //创建一个信息包 Notification notify = //通知名称;谁发起的通知;序列号;发起通知时间;发送的消息 new Notification("jack.hi",this,++seq,System.currentTimeMillis(),"jack"); sendNotification(notify); } }
复制代码
复制代码
package jmx; public interface JackMBean { public void hi(); }
复制代码

这里的类Jack不仅实现了MBean接口,还继承了NotificationBroadcasterSupport。jack在这里创建并发送了一个消息包。

复制代码
package jmx; import javax.management.Notification; import javax.management.NotificationListener; public class HelloListener implements NotificationListener { public void handleNotification(Notification notification, Object handback) { if(handback instanceof Hello) { Hello hello = (Hello)handback; hello.printHello(notification.getMessage()); } } }
复制代码
复制代码
package jmx; import java.lang.management.ManagementFactory; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; public class HelloAgent { public static void main(String[] args) throws JMException, Exception { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); ObjectName helloName = new ObjectName("yunge:name=Hello"); Hello hello=new Hello(); server.registerMBean(hello, helloName); Jack jack = new Jack(); server.registerMBean(jack, new ObjectName("jack:name=Jack")); jack.addNotificationListener(new HelloListener(), null, hello); Thread.sleep(500000); } }
复制代码

我们用Jconsole来进行访问:

这里我们可以看到有两个MBean,一个是yunge,一个是jack。我们执行jack的hi方法后,去看下控制台上的打印信息;

 七、linux下利用JMX监控Tomcat

  利用JMX监控Tomcat,就是相当于部署在tomcat上的应用作为服务端,也就是被管理资源的对象。然后通过程序或者jconsole远程连接到该应用上来。远程连接需要服务器端提供ip和port。如果需要加密访问的话,还需要配置用户名、密码等参数。

主要是在tomcat下的文件catalina.sh中进行一些环境变量的配置配置:

 

-Dcom.sun.management.jmxremote=true                 相关 JMX 代理侦听开关

-Djava.rmi.server.hostname                                     服务器端的IP
-Dcom.sun.management.jmxremote.port=29094             相关 JMX 代理侦听请求的端口

-Dcom.sun.management.jmxremote.ssl=false              指定是否使用 SSL 通讯

-Dcom.sun.management.jmxremote.authenticate=false     指定是否需要密码验证

这样就可以通过客户端或者jconsole对tomcat进行监控。

 

整理jvm其他工具及部分参数

JVM小工具

在${JAVA_HOME}/bin/目录下Sun/Oracle给我们提供了一些处理应用程序性能问题、定位故障的工具, 包含

bin 描述 功能
jps 打印Hotspot VM进程 VMID、JVM参数、main()函数参数、主类名/Jar路径
jstat 查看Hotspot VM 运行时信息 类加载、内存、GC[可分代查看]、JIT编译
jinfo 查看和修改虚拟机各项配置 -flag name=value
jmap heapdump: 生成VM堆转储快照、查询finalize执行队列、Java堆和永久代详细信息 jmap -dump:live,format=b,file=heap.bin [VMID]
jstack 查看VM当前时刻的线程快照: 当前VM内每一条线程正在执行的方法堆栈集合 Thread.getAllStackTraces()提供了类似的功能
javap 查看经javac之后产生的JVM字节码代码 自动解析.class文件, 避免了去理解class文件格式以及手动解析class文件内容
jcmd 一个多功能工具, 可以用来导出堆, 查看Java进程、导出线程信息、 执行GC、查看性能相关数据等 几乎集合了jps、jstat、jinfo、jmap、jstack所有功能
jconsole 基于JMX的可视化监视、管理工具 可以查看内存、线程、类、CPU信息, 以及对JMX MBean进行管理
jvisualvm JDK中最强大运行监视和故障处理工具 可以监控内存泄露、跟踪垃圾回收、执行时内存分析、CPU分析、线程分析…

VM常用参数整理

参数 描述
-Xms 最小堆大小
-Xmx 最大堆大小
-Xmn 新生代大小
-XX:PermSize 永久代大小
-XX:MaxPermSize 永久代最大大小
-XX:+PrintGC 输出GC日志
-verbose:gc -
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC时间戳(以基准时间的形式)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:/path/gc.log 日志文件的输出路径
-XX:+PrintGCApplicationStoppedTime 打印由GC产生的停顿时间


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM