发布新日志

  • jvisualvm的使用遇到的问题

    2011-12-20 19:00:58

    在多线程调试的时候用到了java的VisualVM工具,可能会遇到以下问题:

    在windows下由于以下原因会导致 Local Applications Cannot Be Detected (Error Dialog On Startup):
    1. Java程序缺少读写Windows的临时目录的权限
    2. Windows的临时目录是放在非NTFS分区(确切地说,是不支持ACL的分区)
    所以我们必须保证以下两点
    1. 重新授权,保证对Windows的临时目录有足够的权限
    2. 运行需要监控的Java程序时加上参数-XX:+PerfBypassFileSystemCheck

    连接到远程主机
    要从远程应用程序中检索数据,需要在远程 JVM 上运行 jstatd 实用程序。有关如何启动 jstatd 的更多信息 请参见 jstatd - Virtual Machine jstat Daemon(jstatd - 虚拟机 jstat 守护进程)。无法对远程主机上运行的应用程序进行性能分析。

    1需要在被监控的服务器上面,通过jstatd来启动RMI服务。新建个文件jstatd.all.policy 文件内容如下:
    grant codebase "file:${java.home}/../lib/tools.jar" {
    permission java.security.AllPermission;
    };

    2启动jstatd
    jstatd -J-Djava.security.policy=jstatd.all.policy &

    jstatd -J-Djava.security.policy=/home/admin/jstatd.java.policy -J-Djava.rmi.server.logCalls=true
    上面启动的RMI服务的端口是1099。启动通过:netstat -an|grep 8888可以查看端口是否已经被监听

    同时需要执行一个命令看看(linux需要):

    Hostname –i

    如果显示是127.0.0.1,则需要修改/etc/hosts文件,去掉对本机名的配置,让本机名解析到它的IP地址,如10.20.131.214


    3连接远程主机
    • 右键单击“应用程序”窗口中的“远程”节点,然后选择“添加远程主机”。
    • 在“添加远程主机”对话框中,键入远程计算机的主机名或 IP 地址。
    • (可选)键入远程主机的显示名称。此名称将显示在“应用程序”窗口中。如果没有输入显示名称,则在“应用程序”窗口中使用主机名标识远程主机。
    • 单击“确定”。

  • 转:什么是JMX

    2011-12-20 14:26:29

    一、什么是JMX

    JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,实际上,用户可以在任何Java应用程序中使用这些代理和服务实现管理。

    JMX的前身是JMAPI。

    JMX致力于解决分布式系统管理的问题,因此,能够适合于各种不同的环境是非常重要的。为了能够利用功能强大的Java计算环境解决这一的问题,Sun公司扩充了Java基础类库,开发了专用的管理类库。

    JMX是一种应用编程接口,可扩充对象和方法的集合体,可以用于跨越一系列不同的异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用它提供了用户界面指导、Java类和开发集成系统、网络及网络管理应用的规范。

    管 理对象是JMX应用程序的核心。JMX结构包括:支持Java的Web浏览器用户接口,管理运行模块ARM(Admin Runtime Module)和应用。这三个部件之间通过RMI(Remote Method Invocation)进行通信。这里需要说明的是,RMI是使得一个Java虚拟机(JVM)上运行的程序可以调用远程服务器上另一个JVM总的对象。

    用户接口用来发布管理操作,这些操作可以间接的通过浏览器或通过单独的应用程序来激发。管理运行模块用来给应用提供实例化的管理对象。它包括Agent对象接口,通知接口和被管数据接口。应用指的是那些被管设备单元。

    JMX是一个完整的网络管理应用程序开发环境,它同时提供了:厂商需要收集的完整的特性清单,可生成资源清单表格,图形化的用户接口;访问SNMP的网络API;主机间远程过程调用;数据库访问方法。

    JMX这一轻型的管理基础结构,价值在于对被管理资源的服务实现了抽象,提供了低层的基本类集合,开发人员在保证大多数的公共管理类的完整性和一致性的前提下,进行扩展以满足特定网络管理应用的需要。

    JMX注重于构造管理工具的软件框架,并尽量采用已成熟的技术。

    JMX体系被分成三个层次

    。表现层
    。代理层
    。分布式服务层


    JMX框架

     基本概念

      JMX涉及到以下基本概念。

    MBean:暴露用来操作和访问被管资源的管理接口的java对象
    MBean Server:管理一组MBean的java类,类似一个查找Mbean的注册表,暴露出所有注册过的Mbean的管理接口,以及提供查找Mbean和通知监听器的方法。
    MBean Agent:提供管理MBean的服务的一个Java进程,是MBean Server的容器。提供这些服务:建立MBean关系、动态加载类、简单监控服务、定时器等。
     MBean Agent可以指望有一组协议适配器(Adaptor)或连接器(Connector),使远程客户和不同客户使用agent。协议适配器和连接器通常也是MBean。
    协议适配器(Adaptor)或连接器(Connector):在MBean Agent之中的一些对象,用来将Agent暴露给管理应用和协议。
    Adaptor和Connector区别: Adaptor通常要监听进来的信息,这些信息是在某个协议如HTTP或SNMP中构造的。在这个意义上,协议适配器在任何时间都存在于Agent中并只 有一个组件。Connector由两个组件组成,一个在Agent端,一个在客户端。客户使用客户端连接器组件和服务器端连接器组件联系并和Agent通 讯。

      下图为RMI Connector的两个组件通讯情况:
     

    管理应用:连接任意数量MBean Agent的用户应用。
     JMX Agent可以通过定制开发适配器或连接器,和一个非jmx管理应用进行交互。
    通知(Notification):由MBean或MBean Server发出的,封装了事件、警告和通用信息的Java对象。MBean或Java对象可以注册为监听者来接收通知。JMX的通知模型类似于Java的事件模型。
    设备(Instrumentation):使用MBean或一组MBean暴露管理资源的进程。
    管理应用(Manager Appliction):使用MBean的管理应用程序。

    JMX的三层架构

    分布层(Distributed Layer):包含了能使管理应用和JMX Agent通讯的组件
    代理层(Agent Layer):包含了Agents和MBean Server
    装备层(Instrument Layer):包含了可以代表可管理资源的MBean

    1、分布层:jmx的最外层,负责向外界提供jmx的agent。

      有两种类型的分布式交互(interaction),即建立一个连接(Connectin):
      1) 通过Adaptor获取的交互方式:通过不同的协议如HTTP,SNMP提供到MBean的可见性(visibility)。
      2) JMX Agent有Connector组件,将agent的API暴露给其他分布式技术,如RMI。

     
      当远程客户端通过Adaptor或Connector和Agent建立连接后,就可以和agent中注册的MBean进行交互。接着就进入了代理层。

    2、代理层:

    代理层的主要组件是MBean Server,作为MBean的登记处,是代理层的核心。
    代理层提供4个代理服务来更方便地管理MBean:定时器、监控、动态MBean 加载、关系服务。
    代理层提供从管理应用到被管理资源的访问。
    Jmx代理可以运行在管理资源的机器上的JVM中,也可以位于在远程。agent不需要知道它暴露出的资源的信息,或使用MBean的管理应用。
    agent担当了一个服务的角色,用来处理MBean,允许通过暴露出的Connector或Adaptor的一系列协议来操作MBean。

    3、装备层:离被管理资源最近的一层。由注册在Agent上的MBean组成。

    每个MBean暴露一个底层资源的一块配置和功能,并通过一个Java对象来提供。如果底层资源不使用Java, 则MBean充当一个翻译器。
    MBean是一个轻量级的类,知道如何使用、获取操作其资源,并向agent和用户提供访问途径和功能。
     

    使用JMX作为应用程序架构:
     

      jmx 代理层用来构件应用很理想。MBean Server可以用作应用组件,如数据层、日志组件、事务管理器的骨架。

      使用这种架构,开发人员可以很容易地从服务器中增加、改变、删除应用服务。



    二、表现层

    表现层定义的是JMX可管理资源所在的层。

    这些已管理的资源可以编写或封装为MBean。

    MBean分为四种类型:
    标准、动态、开放、模型

    三、代理层

    定义的是MBean向应用程序施加管理的层。包括MBean服务器和代理服务的定义。还至少包括一个协议适配器或连接器。

    代理的组成:
    。一个MBean服务器
    。要管理的MBean
    。作为MBean实现的代理服务
    。至少一个协议适配器

    3.1 MBean

    在JMX中,最小的可管理单元是MBean。

    MBean不是一个真正的接口和类,而是必须准循的设计模式以确保资源是一个兼容的MBean。

    MBean的激活方法:

    invoke()方法是管理应用程序用于激活MBean的方法的手段。
    invoke()方法有三个参数,分别是方法的名称、表示属性的对象数组和描述方法签名的一个字符串数组。它与使用Java Reflection Api 激活方法的方式类似。

    Notification(通知)模型:

    MBean的通知模型类似与Java事件的监听器模型。
    MBean或管理应用程序可以作为MBean事件的监听器注册。
    通知支持由两个基本的概念组成,即广播器和监听器。

    3.2 MBean服务器

    MBean服务器用于注册可管理的MBean。

    所有对MBean的请求或查询都是通过MBean服务器实施的。

    3.3 代理服务

    代理服务是一些特殊的函数,代理把这些服务提供给MBean,这些代理服务独立于任何MBean。

    一个代理可以提供四种主要的服务:
    。动态装载:允许代理通过下载这个Bean的类动态实例化MBean,这与装载applet的方式类似。
    JMX使用m-let这个标记指定期望动态装载的MBean的位置。
    。监控:允许代理通知有关的监听器监控一个MBean的属性值的变化
    JMX允许使用三种基本类型的监控程序
    (1)CounterMonitor:该程序可以观察类型方面的变化,比如Byte类似或Integer类型
    (2)GaugeMonitor:该程序可以观察类型方面的变化,比如Byte类似或Integer类型,还可以在到达上下阀值时进行报告。
    (3)StringMonitor:该程序可以观察java.lang.String类型的变化。
    。计时器:允许预设的时间间隔发送通知,可以作为一个调度程序
    。关系:允许创建和维护MBean之间的关系

    四、分布式服务层

    包含协议适配器和连接器两种类型的组件,通过它们连接到外部的应用,如RMI管理应用、基于浏览器的管理控制等

    五、协议适配器和连接器

    协议适配器是代理与管理客户通信的手段,每个通信的协议都可能不同。

    它们都是管理客户与MBean交互的方式。

    六、JMX的好处

    。可减少对JAVA应用实施管理的投资
    。提供了一个可伸缩的管理框架
    。集成现有的管理方案:如:WBEM,SNMP,TMN
    。使用现有的标准JAVA技术
    。能使用未来的一些管理概念:如Jini连接技术、通用即插即用、服务定位协议(Service Location Protocol)
    。只定义了一些可以访问的接口

    七、 Getting Started:简单MBean

      运行本文程序需要在JDK5.x环境下,并且要到 java.sun.com网站上去下载Sun的JDMK5.1(Java Dynamic Management Kit 5.1),下载后解压,将lib目录下的jdmktk.jar文件加到项目的类路径上即可(如果找不到,可以向我索取 chengy@primeton.com )。

      1. 定义接口4. 运行
    在IE中打入http://localhost:9092/

    1. package jmxbook.ch2;
    2.  
    3. public interface HelloWorldMBean {
    4. public void setGreeting(String greeting);
    5.  
    6. public String getGreeting();
    7.  
    8. public void printGreeting();
    9. }

    2. 编写Mbean实现类

    1. package jmxbook.ch2;
    2.  
    3. public class HelloWorld implements HelloWorldMBean {
    4. private String greeting=null;
    5.  
    6. public HelloWorld() {
    7. this.greeting="I'm a standard MBean";
    8. }
    9.  
    10. public HelloWorld(String greeting) {
    11. this.greeting=greeting;
    12. }
    13.  
    14. public String getGreeting() {
    15. return greeting;
    16. }
    17.  
    18. public void printGreeting() {
    19. System.out.println(greeting);
    20. }
    21.  
    22. public void setGreeting(String greeting) {
    23. this.greeting=greeting;
    24. }
    25. }

    3. 编写Agent

    1. package jmxbook.ch2;
    2.  
    3. import javax.management.MBeanServer;
    4. import javax.management.MBeanServerFactory;
    5. import javax.management.ObjectName;
    6. import com.sun.jdmk.comm.HtmlAdaptorServer;
    7.  
    8. public class HelloAgent {
    9. private MBeanServer server = null;
    10.  
    11. public HelloAgent() {
    12. server = MBeanServerFactory.createMBeanServer("HelloAgent");
    13.  
    14. HtmlAdaptorServer adapter = new HtmlAdaptorServer();
    15.  
    16. HelloWorld hw = new HelloWorld();
    17.  
    18. ObjectName adapterName = null;
    19. ObjectName mbeanName = null;
    20.  
    21. try {
    22. mbeanName = new ObjectName("HelloAgent:name=helloWrold");
    23. server.registerMBean(hw, mbeanName);
    24.  
    25. adapterName = new ObjectName(
    26. "HelloAgent:name=htmlAdaptor,port=9092");
    27. adapter.setPort(9092);
    28. server.registerMBean(adapter, adapterName);
    29. adapter.start();
    30.  
    31. } catch (Exception e) {
    32. e.printStackTrace();
    33. }
    34.  
    35. }
    36.  
    37. public static void main(String args[]) {
    38. System.out.println("HelloAgent is running");
    39. HelloAgent agent = new HelloAgent();
    40. }
    41. }

     说明:
    1) 区分Mbean: 使用ObjcetName对象,分两部分:1) 域名 2) key-value对列表
    如 HelloAgent:name=helloWorld

    两个ObjectName的相等:
    ObjectName objName1=new ObjectName(“HelloAgent:name=helloWorld,type=typeA”);
    ObjectName objName2=new ObjectName(“HelloAgent: type=typeA,name=helloWorld”);

    则objName1.equals(objName2)返回true.

    2) ObjectName冲突: MBean Server注册两个相同ObjectName的MBean会抛出异常.

    5.使用html客户端访问jmx agent:

    1) Agent View: 显示所有注册的MBean
     
    2) MBean View: 显示一个MBean信息
     
    3) Admin View: 管理MBean,注册和取消注册MBean
     

      输入key和java className, action选择Constrctors, 可以查看类的构造函数列表(需要MBean的接口命名为  名称+MBean, 如HelloWorldMBean), 输入构造函数的参数(有的话),按create可以建立新的MBean并注册到agent.
     

    四.使用通知:

      jmx可以使用通知机制,从一个MBean发送通知给另一个MBean,如下图:

     
    使用通知的步骤如下:
    1) 建立通知发送者:
    两种方式:
    (1) 实现javax.management.NotificationBroadcaster接口
    (2) 扩展 javax.management.NotificationBroadcasterSupport类

    改造HelloWorld.java

    1. package jmxbook.ch2.notification;
    2.  
    3. import javax.management.Notification;
    4. import javax.management.NotificationBroadcasterSupport;
    5.  
    6. public class HelloWorld extends NotificationBroadcasterSupport
    7. implements HelloWorldMBean {
    8. public HelloWorld() {
    9. this.greeting = "I'm a Notification Sender";
    10. }
    11.  
    12. public HelloWorld(String greeting) {
    13. this.greeting = greeting;
    14. }
    15.  
    16. public void setGreeting(String greeting) {
    17. this.greeting = greeting;
    18. Notification notification = new Notification(
    19. "jmxbook.ch2.helloWorld.test", this, -1, System
    20. .currentTimeMillis(), greeting);
    21. sendNotification(notification);
    22. }
    23.  
    24. public String getGreeting() {
    25. return greeting;
    26. }
    27.  
    28. public void printGreeting() {
    29. System.out.println(greeting);
    30. }
    31.  
    32. private String greeting;
    33. }


     2) 建立通知接收者:
    接口: MyListenerMBean.java:

    1. package jmxbook.ch2.notification;
    2.  
    3. import javax.management.NotificationListener;
    4.  
    5. public interface MyListenerMBean extends NotificationListener {
    6. public void printInfo(String message);
    7. }


    实现类: MyListener.java

    1. package jmxbook.ch2.notification;
    2.  
    3. import javax.management.Notification;
    4.  
    5. public class MyListener implements MyListenerMBean {
    6.  
    7. public void printInfo(String message) {
    8. System.out.println(message);
    9. }
    10.  
    11. public void handleNotification(Notification notification, Object handback) {
    12. this.printInfo("My listener recieve Nofitication: " + notification.getType() + " "
    13. + notification.getMessage());
    14. }
    15. }

    3) 改造HelloAgent:

    1. package jmxbook.ch2.notification;
    2.  
    3. import javax.management.*;
    4. import com.sun.jdmk.comm.HtmlAdaptorServer;
    5.  
    6. public class HelloAgent implements NotificationListener {
    7. private MBeanServer mbs = null;
    8.  
    9. public HelloAgent() {
    10. mbs = MBeanServerFactory.createMBeanServer("HelloAgent");
    11. HtmlAdaptorServer adapter = new HtmlAdaptorServer();
    12.  
    13. HelloWorld hw = new HelloWorld();
    14. ObjectName adapterName = null;
    15. ObjectName helloWorldName = null;
    16. try {
    17. adapterName = new ObjectName(
    18. "HelloAgent:name=htmladapter,port=9092");
    19. mbs.registerMBean(adapter, adapterName);
    20. adapter.setPort(9092);
    21. adapter.start();
    22.  
    23. MyListener listener = new MyListener();
    24. mbs.registerMBean(listener, new ObjectName(
    25. "HelloAgent:name=myListener"));
    26.  
    27. helloWorldName = new ObjectName(
    28. "HelloAgent:name=helloWorld,notification=yes");
    29. mbs.registerMBean(hw, helloWorldName);
    30. hw.addNotificationListener(this, null, null);
    31. hw.addNotificationListener(listener, null, null);
    32. } catch (Exception e) {
    33. e.printStackTrace();
    34. }
    35. }// constructor
    36.  
    37. public void handleNotification(Notification notif, Object handback) {
    38. System.out.println("My listener recieve Nofitication: "
    39. + notif.getType() + " " + notif.getMessage());
    40. }
    41.  
    42. public static void main(String args[]) {
    43. HelloAgent agent = new HelloAgent();
    44. System.out.println("HelloAgent is running");
    45. }
    46. }

    4) 运行:
    调用HelloWorld的greeting(“Can I help you?”)方法, 控制台显示:
    Agent recieve Nofitication: jmxbook.ch2.helloWorld.test Can I help you?
    My listener recieve Nofitication: jmxbook.ch2.helloWorld.test Can I help you?

    五.使用RMI Connector:

    1) 改造Agent:

    1. package jmxbook.ch3;
    2.  
    3. import com.sun.jdmk.comm.*;
    4. import javax.management.*;
    5.  
    6. public class JMXBookAgent {
    7. private MBeanServer server = null;
    8.  
    9. public JMXBookAgent() {
    10. System.out.println("/n/tCREATE the MBeanServer.");
    11. server = MBeanServerFactory.createMBeanServer("JMXBookAgent");
    12. startHTMLAdapter();
    13. startRMIConnector();
    14. }
    15.  
    16. protected void startHTMLAdapter() {
    17. HtmlAdaptorServer adapter = new HtmlAdaptorServer();
    18. ObjectName adapterName = null;
    19. try {
    20. adapter.setPort(9092);
    21. adapterName = new ObjectName("JMXBookAgent:name=html,port=9092");
    22. server.registerMBean(adapter, adapterName);
    23. adapter.start();
    24. } catch (Exception e) {
    25. ExceptionUtil.printException(e);
    26. System.out.println("Error Starting HTML Adapter for Agent");
    27. }
    28. }
    29.  
    30. protected void startRMIConnector() {
    31. RmiConnectorServer connector = new RmiConnectorServer();
    32. ObjectName connectorName = null;
    33. try {
    34. connector.setPort(2099);
    35. connectorName = new ObjectName("JMXBookAgent:name=RMIConnector");
    36. server.registerMBean(connector, connectorName);
    37. connector.start();
    38. } catch (Exception e) {
    39. ExceptionUtil.printException(e);
    40. }
    41. }
    42.  
    43. public static void main(String[] args) {
    44. System.out.println("/n~~~~~~~~~~~~~~~~~~~~~~~"
    45. + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    46. System.out.println("/n>>> START of JMXBook Agent");
    47. System.out.println("/n>>> CREATE the agent...");
    48. JMXBookAgent agent = new JMXBookAgent();
    49. System.out.println("/nAgent is Ready for Service.../n");
    50. }
    51. }


    2) 添加异常显示类

    1. package jmxbook.ch3;
    2.  
    3. import javax.management.*;
    4.  
    5. public class ExceptionUtil {
    6. public static void printException(Exception e) {
    7. System.out.println("-------[ Exception ]-------");
    8. e.printStackTrace();
    9. if (e instanceof MBeanException) {
    10. boolean hasEmbeddedExceptions = true;
    11. Exception embeddedExc = e;
    12. while (hasEmbeddedExceptions) {
    13. embeddedExc = ((MBeanException) embeddedExc)
    14. .getTargetException();
    15. System.out.println("-------[ Embedded Exception ]-------");
    16. embeddedExc.printStackTrace();
    17. if (!(embeddedExc instanceof MBeanException)) {
    18. hasEmbeddedExceptions = false;
    19. }
    20. }
    21. }
    22. }
    23. }

    3) RMI 工厂

    1. package jmxbook.ch3;
    2.  
    3. import com.sun.jdmk.comm.RmiConnectorAddress;
    4. import com.sun.jdmk.comm.RmiConnectorClient;
    5.  
    6.  
    7. public class RMIClientFactory {
    8. public static RmiConnectorClient getClient() {
    9. RmiConnectorClient client = new RmiConnectorClient();
    10. RmiConnectorAddress address = new RmiConnectorAddress();
    11. address.setPort(2099);
    12. System.out.println("/t/tTYPE/t= " + address.getConnectorType());
    13. System.out.println("/t/tPORT/t= " + address.getPort());
    14. System.out.println("/t/tHOST/t= " + address.getHost());
    15. System.out.println("/t/tSERVER/t= " + address.getName());
    16. try {
    17. client.connect(address);
    18. } catch (Exception e) {
    19. ExceptionUtil.printException(e);
    20. }
    21. return client;
    22. }
    23. }

    5) 建立RMI客户端:

    1. package jmxbook.ch3;
    2.  
    3. import javax.management.*;
    4. import jmxbook.ch2.*;
    5. import com.sun.jdmk.comm.*;
    6.  
    7. public class MBeanSetup {
    8. public MBeanSetup() {
    9. try {
    10. RmiConnectorClient client = RMIClientFactory.getClient();
    11. ObjectName hwName = new ObjectName("JMXBookAgent:name=helloWorld");
    12. client.createMBean("jmxbook.ch2.HelloWorld", hwName);
    13. client.invoke(hwName, "printGreeting", null, null);
    14. } catch (Exception e) {
    15. e.printStackTrace();
    16. }
    17. }
    18.  
    19. public static void main(String args[]) {
    20. MBeanSetup setup = new MBeanSetup();
    21. }
    22. }

    6) 运行:
    运行agent:

    1. java -cp ../lib/jdmkrt.jar;. jmxbook.ch3.JMXBookAgent
    2.  
    3. >>> START of JMXBook Agent
    4.  
    5. >>> CREATE the agent...
    6.  
    7. CREATE the MBeanServer.
    8.  
    9. Agent is Ready for Service...

    运行客户端:

    1. java -cp ../lib/jdmkrt.jar;. jmxbook.ch3.MBeanSetup
    2.  
    3. TYPE = SUN RMI
    4. PORT = 2099
    5. HOST = chengy
    6. SERVER = name=RmiConnectorServer
  • 使用JConsole监控进程、线程、内存、cpu、类情况

    2011-12-20 13:51:30

    Jconsole简介:

    Jconsole是一个JMX兼容的监视工具。它使用Java虚拟机的JMX机制来提供运行在Java平台的应用程序的性能与资源耗费信息。


    监控进程使用方法如下:
    由于JConsole监控需要使用JMX代理技术,因此在启动应用程序的参数中添加以下几个参数:
    -Djava.rmi.server.hostname=192.168.1.80
    -Dcom.sun.management.jmxremote.port=8089
    -Dcom.sun.management.jmxremote.ssl=false
    -Dcom.sun.management.jmxremote.authenticate=false

    配置完以上参数后,本地启动JConsole后,选择相应的进程名称,点击连接后,就可监控了。


    关于JMX的介绍:
    http://www.51testing.com/?uid-115295-action-viewspace-itemid-804845

    其他使用:

    Jconsole界面:

    Jconsole界面由以下六个选项卡组成:

    Summary选项卡:显示JVM和被监视值的汇总信息

    Memory选项卡:显示内存使用信息。

    Threads选项卡:显示线程使用信息。

    Classes选项卡:显示类(class)加载信息。

    MBeans选项卡:显示MBeans信息

    VM选项卡:显示JVM信息。

    以下是详细介绍:

    查看汇总信息:

    Summary选项卡显示了关于线程使用、内存消耗和class加载的一些关键监视信息,以及JVM和操作系统的信息。

    clip_image008

    Summary

    UptimeJVM已运行时长。

    Total compile time花费在即时编译(JIT compilation)中的时间。

    Process CPU timeJVM花费的总CPU时间。

    Threads

    Live threads当前活动的daemon线程加non-daemon线程数量。

    Peak自JVM启动后,活动线程峰值。

    Daemon threads当前活动的Daemon线程数量。

    Total started自JVM启动后,启动的线程总量(包括daemon,non-daemon和终止了的)

    Memory

    Current heap size堆(heap)占用的内存量,以K为单位。

    Committed memory为堆分配的内存总量

    Maximum heap size堆占用的最大内存量。

    Objects pending for finalization等待析构(finalization)的对象数量。

    Garbage collector informationGC信息,摆阔垃圾回收器名称,已执行的垃圾回收次数和执行垃圾回收总耗时。

    Classes

    Current classes loaded当前被加载到内存的classes数量

    Total classes loaded自JVM启动后被加载到内存的classes总量,包括后来卸载的。

    Total classes unloaded自JVM启动后,从内存卸载的classes总量。

    Operating System:

    Total physical memory物理内存总量

    Free physical memory物理内存空闲量

    Committed virtual memory为运行中的进程分配的虚拟内存总量

    监视内存消耗:

    Memory选项卡提供了内存消耗和内存池信息。

    clip_image010

    以上图表显示了JVM的内存使用和时间的对应关系,包括heap和non-heap内存以及指定的(specific)内存池。内存池种类与具体使用的JVM有关,以HotSpot JVM为例,内存池有:

    l Eden Space(heap):大多数对象初始化时从Eden Space池分配内存,即是存在于此池中

    l Survivor Space(heap):此池包含的对象是那些原先在eden space中,但是已经经历过垃圾回收而仍然存在的对象。

    l Tenured Generation(heap):在surviver space中已经存在了一段时间之后的对象会移动到这个池中。

    l Permanent Generation(non-heap):包含虚拟机自身的所有反射数据。比如class和mothod对象。对于使用class data sharing的JVM,这一代分为只读和读写两个区域。

    Code Cache (non-heap):HotSpot JVM也包含一个“代码缓存”,是编译和存储本地代码所占用的内存。

    查看关于内存池的详细信息:Garbage Collection.

    Detail区域显示了几种当前内存度量:

    lUsed当前使用的内存总量。使用的内存总量是指所有的对象占用的内存,包括可达和不可达的对象。

    lCommittedJVM可使用的内存量。Committed内存数量可能随时间变化而变化。JAVA虚拟机可能将某些内存释放,还给操作系统,committed内存可能比启动时初始分配的内存量要少。Committed内存总是大于等于used内存。

    lMax内存管理可用的最大内存数量。此值可能改变或者为未定义。如果JVM试图增加使用内存(used memory)超出了committed内存,那么即时使用内存小于或者等于最大内存(比如系统虚拟内存较低),内存分配仍可能失败。

    右下角的图表显示了内存池在heap和non-heap消耗的内存量。当内存使用超出了内存使用阀值时,柱状图会变红。你可以通过设置MemoryMXBean的一个属性来调整内存占用阀值。

    Heap and Non-heap内存

    JVM管理两种内存:heap和non-heap内存,两种内存都是在JVM启动时建立。

    Heap memory是运行时数据区域,用于JVM为所有对象实例和队列分配的内存。Heap可能为固定植或者可变值。垃圾收集器是一个用于回收对象占用的heap内存的自动化内存管理系统。

    Non-heap memory 包含一个在所有线程共享的方法区域(method area)和内部进程或JVM优化所需的内存。它存储了每一个类的结构,比如运行常量池,字段和方法数据,构造函数和方法的代码。方法区域逻辑上是 heap的一部分,但是依赖于实现,JVM可能不进行垃圾收集或压缩。像heap一样,方法区域可能为固定或可变大小。方法区域所需要的内存没有必要是连 续的。

    除了方法区域之外,一个JVM实现的内部进程或优化所需的内存也属于non-heap内存。比如JIT编译器为了提高性能而用于存储本地机器码所需的内存。

    内存池和内存管理

    内存池(Memory pools)和内存管理器是JVM内存管理系统的关键部分

    一个内存池(memory pool)代表JVM管理的一块内存区域。JVM拥有最少一个内存池,JVM在运行中可能创建或删除内存池。一个内存池可以属于heap内存或者non-heap内存。

    内存管理器(memory manager)管理一个或多个内存池。垃圾回收其是一种负责回收被不可打对象占用的内存的内存管理器。一个JVM可以拥有一个或者多个内存管理器。JVM在运行中可能增加或删除内存管理器。一个内存池可以被多于一个内存管理器管理。

    垃圾收集:

    垃圾收集(GC)是指JVM释放那些被无引用对象占用的内存空间。它通常认为那些有活动引用的对象是“活”对象,而那些没有引用或不可达的对象为“死”对象”。垃圾收集是释放被死对象占用的内存的过程。GC的算法和参数对性能有巨大的影响。

    HotSpot VM垃圾收集器使用 分代垃圾收集(generational garbage collection)。分代GC利用了大多数程序中,从经验看有如下特点:

    很多对象有一个很短的生存期(比如迭代器iterators、本地变量)

    某些对象拥有很长的生存期(比如高层持久化对象)

    所 以,分代的GC将内存划分为代(generations)并且赋予每一个内存池。当一代用尽了分配的内存,VM会在那个内存池进行一次局部 (partial)的垃圾收集(或者叫minor collection)来收集被死对象占用的内存。局部垃圾收集比全垃圾收集(full GC)快的多。

    HotSpot VM定义了2代:young generation (有时叫做nursery)和old generation。Young generation由一个eden space和两个survivor spaces组成。最初,VM将所有的对象放入eden space,大多数对象死在那里~~~,当VM运行了一次minor GC,VM将剩余的对象从eden space移动到某个survivor space中。然后VM将那些在survivor spaces中生存了足够长时间的对象移动到位于old generation中的tenured spaces。当tenured spaces满了以后,将发生一次full GC,full GC涉及到所有存活的对象,因此比较慢。Permanent generation保存了所有的虚拟机自身的反射数据,比如class和method objects

    默认情况下代的排列类似于下图:

    clip_image012

    如同下文链接中说明的,如果垃圾收集器成为瓶颈,你可以通过自定义代大小来提高性能。使用jconsole可以发现你的性能情况对垃圾收集器参数的敏感程度。详细情况见:

    Tuning Garbage collection with the 5.0 HotSpot VM

    监视线程使用:

    线程选项卡提供了关于线程使用的信息。

    clip_image014

    左下角列出的为所有的活动线程。如果你在过滤(filter)对话框输入字符串,那么线程列表将尽显示那些包含你输入字符串的线程。在线程列表上点击线程名,可以显示在右侧显示县城信息,包括线程名,状态和调用堆栈。

    图表显示了活动线程/时间。有三行内容:

    Magenta:线程总数

    Red:峰值线程数

    Blue:活动线程数。

    关于线程、daemon线程详细信息,请查看链接:java.lang.Thread

    监视Class加载:

    Classes选项卡显示了关于class loading的信息:

    clip_image016

    图表显示了 类加载/时间

    红线是类加载总数(包括后来卸载的)

    蓝线表示当前的类加载数量。

    选项卡底部的Detail节显示了自JVM启动后类加载的总量,当前加载量和卸载量。

    监视和管理MBeans:

    MBean选项卡显示了所有在platform. MBean server上注册的MBeans的信息。

    clip_image018

    左边的树形结构显示了所有的MBean,按其对象名排序。当在树种选择了一个MBean之后,其属性、操作、通知和其他信息会在右边显示。

    如果属性值是可写的(可写会蓝色显示),你可以设置属性值。你也可以调用在操作选项卡中显示的操作。

    显示图表:

    你可以通过双击属性值的方法显示一个属性值/时间图表,比如,如果你双击java.lang.GarbageCollector.Copy Mbean的CollectionTime属性,你会得到如下图所示的显示:

    clip_image020

    查看VM信息。

    VM选项卡提供了JVM的信息。

    clip_image022

    这些信息包括:

    Uptime:JVM启动后的总时间。

    Processes CPU Time:JVM启动后消耗的总CPU时间。

    Total Compile Time:即时编译(JIT compilation)消耗的总时间。JVM的具体实现决定JIT编译何时发生。Hotspot VM使用adaptive compilation,在这种方式zhogn ,VM使用标准的解释器(interpreter)运行一个应用程序,但是会分析其代码判断性能瓶颈或者”hot spots”。

    配置tomcat使用jconsole

    修改catalina脚本

    Windows平台:修改catalina.bat,在dorun和dostart段开头增加一行(注意是一行):

    set JAVA_OPTS=%JAVA_OPTS% -Djava.rmi.server.hostname=192.168.1.101

    -Dcom.sun.management.jmxremote

    -Dcom.sun.management.jmxremote.port="9004"

    -Dcom.sun.management.jmxremote.authenticate="false" -Dcom.sun.management.jmxremote.ssl="false"

    Unix/Linux平台:修改catalina.sh,在dorun和dostart段开头增加一行(注意是一行):

    JAVA_OPTS="$JAVA_OPTS "-Dcom.sun.management.jmxremote

    -Dcom.sun.management.jmxremote.port="9004"

    -Dcom.sun.management.jmxremote.authenticate="false" -Dcom.sun.management.jmxremote.ssl="false"

    启动jconsole

    启动tomcat之后,根据上文中的jconsole简介中的命令启动jconsole,如果是在服务器本地运行jconsole,会出现如下界面:

    clip_image023

    直接进行连接即可。

    如果是远程监控,需要点击远程选项卡并输入相关信息,示例如下:

    clip_image024

    “主机名或ip”处填写需要监视的主机ip,端口为服务器上上文中添加的-Dcom.sun.management.jmxremote.port="portNumber"设定的端口,本文以9004为例。在设定为:-Dcom.sun.management.jmxremote.authenticate="false" 的情况下,用户名和口令留空即可。

     

    如果需要使用JConsole远程监控 Tomcat可以在命令行直接输入:

    JConsole 192.168.1.101:9004

    进阶安全设定

    在上文中的配置适用于在测试环境中监视tomcat,如果是在生产环境中监视tomcat则需要在安全性上有进一步要求。

    配置jmx访问密码

    1. 修改上文中的catalina脚本中的JAVA_OPT参数,将
    -Dcom.sun.management.jmxremote.authenticate="false" 修改为:
    -Dcom.sun.management.jmxremote.authenticate="true"

    2. 将$JRE/lib/management/jmxremote.password.template文件在同目录下复制一份,重命名为$JRE/lib /management/jmxremote.password,编辑jmxremote.password,添加允许访问的用户名及密码,比如添加用户 zxwh,密码为zxme,则在文件尾添加一行:
    zxwh zxme
    注意用户密码不能包含空格,tab等字符

    3. 编辑$JRE_HOME/lib/management/jmxremote.access文件,对刚才添加的用户赋予一定的权限:
    zxwh readonly (或者readwrite)

    4. 确认jmxremote.password和jmxremote.access两个文件中的用户是相同的。注意如果jmxremote.access中没有对应用户的话,配置是无效的。
    注:以上配置文件的位置都是可以更改的,具体配置方法在此不再赘述。

    5. 由于jmxremote.password中的密码都是明文保存的,所以jmxremote.password、jmxremote.access文件的权限要注意,应该设置为只有owner才可读,当然这个用户也必须是启动tomcat的用户。

    6. 启动jconsole进行连接,在用户名和口令处输入设定的用户和密码。

    7. 使用密码认证方式进行连接,不但可以提高安全性,而且可以对用户的权限进行设置。如果不使用密码认证的方式,则无法对用户的权限进行限制。

    配置使用ssl进行加密连接

    1. 在服务器上使用keytool创建密钥对
    keytool是java平台自带的一个密钥和证书管理工具,使用keytool创建密钥对:
    keytool -genkey -alias tomcat -keystore /somepath/tomcatKeyStore
    按照提示输入相关信息(包括设定密码、姓、组织名等),这些信息是可以随便输入的,但从产品角度讲应该统一设定。输入的密码在今后操作中均需要使用。

    2. 导出公钥
    keytool -export -alias tomcat -keystore /somepath/tomcatKeyStore -file /somepath/jconsole.cert

    3. 将公钥导入至需要运行jconsole的机器。
    keytool –import –alias jconsole –keystore /somepath/jconsoleKeyStore -file /somepath/jconsole.cert

    4. 修改tomcat的catalina脚本
    将-Dcom.sun.management.jmxremote.ssl="false"修改为:
    -Dcom.sun.management.jmxremote.ssl="true",并在 JAVA_OPTS变量行添加:
    -Djavax.net.ssl.keyStore=/somepath/jconsoleKeyStore
    -Djavax.net.ssl.keyStorePassword=设定的密码

    5. 使用如下参数启动jconsole :
    jconsole -J-Djavax.net.ssl.trustStore=/somepath/jconsoleKeyStore

    6. 填入主机名、用户、口令连接服务器。

    其他问题

    1. 在执行shutdown.sh或者shutdown.bat脚本关闭tomcat时出现如下错误,tomcat无法关闭:
    错误: 代理抛出异常: java.rmi.server.ExportException: Port already in use: 9004;
    nested exception is:
    java.net.BindException: Address already in use: JVM_Bind


    其他分析参考:

    http://jiajun.iteye.com/blog/810150

    http://jingyan.baidu.com/article/215817f73a4f941edb14234f.html

    推荐使用升级版 JConsole 即 jvisualvm




  • 转:JDK自带VM分析工具jps,jstat,jmap,jconsole

    2011-12-20 10:28:22

    一、概述  
        SUN 的JDK中的几个工具,非常好用。秉承着有免费,不用商用的原则。以下简单介绍一下这几种工具。(注:本文章下的所有工具都存在JDK5.0以上版本的工具集里,同javac一样,不须特意安装)  。
         
        我一共找到以下四个工具:重点看看jconsole和jmap
    1.jps                
    .:与unix上的ps类似,用来显示本地的java进程,可以查看本地运行着几个java程序,并显示他们的进程号。                    

    2.jstat                
    .:一个极强的监视VM内存工具。可以用来监视VM内存内的各种堆和非堆的大小及其内存使用量。                    
                
    3.jmap                
    .:打印出某个java进程(使用pid)内存内的,所有‘对象’的情况(如:产生那些对象,及其数量)。                    
    4.jconsole                
    .:一个java GUI监视工具,可以以图表化的形式显示各种数据。并可通过远程连接监视远程的服务器VM。    
     
    二、 使用介绍: 
         
        1、jps :我想很多人都是用过unix系统里的ps命令,这个命令主要是用来显示当前系统的进程情况,有哪些进程,及其 id。 jps 也是一样,它的作用是显示当前系统的java进程情况,及其id号。我们可以通过它来查看我们到底启动了几个java进程(因为每一个java程序都会独 占一个java虚拟机实例),和他们的进程号(为下面几个程序做准备),并可通过opt来查看这些进程的详细启动参数。 
        使用方法:在当前命令行下打 jps(需要JAVA_HOME,没有的话,到改程序的目录下打) 。
    可惜没有linux下的ps好用,名称不好用。但是在第四个工具jconsole的界面里面会有具体JAR包的名称。
    /data/jdk1.6.0_06/bin/jps    
    6360 Resin
    6322 WatchdogManager
    2466 Jps

          
        2、jstat :对VM内存使用量进行监控。 
        jstat工具特别强大,有众多的可选项,详细查看堆内各个部分的使用量,以及加载类的数量。使用时,需加上查看进程的进程id,和所选参数。以下详细介绍各个参数的意义。 
        jstat -class pid:显示加载class的数量,及所占空间等信息。 
        jstat -compiler pid:显示VM实时编译的数量等信息。 
        jstat -gc pid:可以显示gc的信息,查看gc的次数,及时间。其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。 
        jstat -gccapacity:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如:PGCMN显示的是最小perm的内存使 用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推, OC是old内纯的占用量。 
        jstat -gcnew pid:new对象的信息。 
        jstat -gcnewcapacity pid:new对象的信息及其占用量。 
        jstat -gcold pid:old对象的信息。 
        jstat -gcoldcapacity pid:old对象的信息及其占用量。 
        jstat -gcpermcapacity pid: perm对象的信息及其占用量。 
        jstat -util pid:统计gc信息统计。 
        jstat -printcompilation pid:当前VM执行的信息。 
        除了以上一个参数外,还可以同时加上 两个数字,如:jstat -printcompilation 3024 250 6是每250毫秒打印一次,一共打印6次,还可以加上-h3每三行显示一下标题。 
         
       3、jmap 是一个可以输出所有内存中对象的工具,甚至可以将VM 中的heap,以二进制输出成文本。使用方法 jmap -histo pid。如果连用 SHELL jmap -histo pid>a.log可以将其保存到文本中去(windows下也可以使用),在一段时间后,使用文本对比工具,可以对比出GC回收了哪些对象。jmap -dump:format=b,file=f1 3024可以将3024进程的内存heap输出出来到f1文件里。 
         
        4、jconsole 是一个用java写的GUI程序,用来监控VM,并可监控远程的VM,非常易用,而且功能非常强。由于是GUI程序,这里就不详细介绍了,不会的地方可以参考SUN的官方文档。 
        使用方法:命令行里打 jconsole,选则进程就可以了。 
         
        友好提示:windows查看进程号,由于任务管理器默认的情况下是不显示进程id号的,所以可以通过如下方法加上。ctrl+alt+del打开任务管 理器,选择‘进程’选项卡,点‘查看’->''选择列''->加上''PID'',就可以了。当然还有其他很好的选项。
     
    实例:
    jmap -histo:live $pid
    jmap -histo $pid
    jstack -l $pid
     
    三、参考资料:
        article:http://elf8848.javaeye.com/blog/442806

        jps:http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jps.html 

        jstat:http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jstat.html 

        jmap:http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jmap.html 

        jconsole:http://java.sun.com/j2se/1.5.0/docs/guide/management/jconsole.html
  • java内存溢出分析工具:jmap使用实战

    2011-12-08 16:33:11

    java内存溢出分析工具:jmap使用实战
    在一次解决系统tomcat老是内存撑到头,然后崩溃的问题时,使用到了jmap。
    1 使用命令
    在环境是linux+jdk1.5以上,这个工具是自带的,路径在JDK_HOME/bin/下
    jmap -histo pid>a.log

    2 输出结果摘要
    Size    Count   Class description
    -------------------------------------------------------
    353371288       9652324 char[]
    230711112       9612963 java.lang.String
    139347160       114865  byte[]
    76128096        3172004 java.util.Hashtable$Entry
    75782280        3157595 com.test.util.IPSeeker$IPLocation
    25724272        9115    java.util.Hashtable$Entry[]
    9319968 166428  org.apache.tomcat.util.buf.MessageBytes
    8533856 32889   int[]

    发现有大量的String和自定义对象com.test.util.IPSeeker$IPLocation存在,检查程序发现此处果然存在内存溢出。修改程序上线后再次用jmap抓取内存数据:

    146881712   207163  byte[]
    98976352    354285  char[]
    42595272    53558   int[]
    11515632    479818  java.util.HashMap$Entry
    9521896 59808   java.util.HashMap$Entry[]
    8887392 370308  com.test.bean.UnionIPEntry
    8704808 155443  org.apache.tomcat.util.buf.MessageBytes
    8066880 336120  java.lang.String

    内存溢出问题消除。
    注意:这个jmap使用的时候jvm是处在假死状态的,只能在服务瘫痪的时候为了解决问题来使用,否则会造成服务中断。
  • 性能测试中报错:Too many open files

    2011-10-12 09:14:00

    在做性能测试过程中,可能会遇到报Too many open files的异常错误,这个问题可能是由于系统的打开文件数受到了限制而导致的,通过以下方法可在一定程度上解决这个问题。不过也有可能是因为程序代码没有释放资源。

    1。ulimit -a查看系统目前资源限制的设定。
    [root@test security]# ulimit -a
    core file size        (blocks, -c) 0
    data seg size         (kbytes, -d) unlimited
    file size             (blocks, -f) unlimited
    max locked memory     (kbytes, -l) unlimited
    max memory size       (kbytes, -m) unlimited
    open files                    (-n) 1024
    pipe size          (512 bytes, -p) 8
    stack size            (kbytes, -s) 8192
    cpu time             (seconds, -t) unlimited
    max user processes            (-u) 7168
    virtual memory        (kbytes, -v) unlimited
    [root@test security]#
    通过以上命令,我们可以看到open files 的最大数为1024
    那么我们可以通过一下命令修改该参数的最大值

    2.ulimit -n 4096
    [root@test security]# ulimit -n 4096
    [root@test security]# ulimit -a
    core file size        (blocks, -c) 0
    data seg size         (kbytes, -d) unlimited
    file size             (blocks, -f) unlimited
    max locked memory     (kbytes, -l) unlimited
    max memory size       (kbytes, -m) unlimited
    open files                    (-n) 4096
    pipe size          (512 bytes, -p) 8
    stack size            (kbytes, -s) 8192
    cpu time             (seconds, -t) unlimited
    max user processes            (-u) 7168
    virtual memory        (kbytes, -v) unlimited

Open Toolbar