如何动态打开verbose class
在一个Java企业环境中,可能会存在这样那样和类相关的情况,比如类冲突(classloader加载的类不是我们所需要的),perm gen内存泄漏等。出现这样的情况,我们可能需要打开verbose class来trace class的load、unload情况。通过打开verbose class的格式如下:
java %JAVA_OPTIONS% %MEM_ARGS% -verbose:class MainClass >tmp.out 2>&1
上述这种方法需要我们重新启动服务器,比如Tomcat、Weblogic等。但如果我们运行的是一个生产系统,重启不是个好的方法。那么除了上述方法,我们能否在不重启服务器的情况下动态打开、关闭verbose class呢?
JDK的MBean为我们提供了这样的接口,我们可以通过这样的MBean来实现上述需求。MBean提供了大量JVM管理的接口,比如Memory、GarbageCollector、ClassLoading、Thread、Runtime等,更详细的信息可以参考 javax.management的API。下面我们看看如果通过程序的方式attach上target JVM,然后打开verbose class。
对于attach到target jvm我们可以使用如下两种方式:
1:根据process id,即pid
如何获取pid呢?
Windows: netstat -abn | find "7001", 7001是服务器的listening port
Linux/Unix: ps -ef | grep java
1
public
MBeanServerConnection getMBeanServerConnection(
long
pid)
2 {
3 MBeanServerConnection serviceConnection = null ;
4 VirtualMachine vm = null ;
5 try
6 {
7 vm = VirtualMachine.attach(String.valueOf(pid));
8 String connectorAddress =
9 vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
10 if (connectorAddress == null ) {
11 String agent = vm.getSystemProperties().getProperty( " java.home " ) +
12 File.separator + " lib " + File.separator + " management-agent.jar " ;
13 vm.loadAgent(agent);
14 connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
15 }
16 if (connectorAddress == null )
17 System.exit( 1 );
18 serviceConnection = getMBeanServerConnection(connectorAddress);
19 } catch (Exception e){}
20 return serviceConnection;
21 }
2 {
3 MBeanServerConnection serviceConnection = null ;
4 VirtualMachine vm = null ;
5 try
6 {
7 vm = VirtualMachine.attach(String.valueOf(pid));
8 String connectorAddress =
9 vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
10 if (connectorAddress == null ) {
11 String agent = vm.getSystemProperties().getProperty( " java.home " ) +
12 File.separator + " lib " + File.separator + " management-agent.jar " ;
13 vm.loadAgent(agent);
14 connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
15 }
16 if (connectorAddress == null )
17 System.exit( 1 );
18 serviceConnection = getMBeanServerConnection(connectorAddress);
19 } catch (Exception e){}
20 return serviceConnection;
21 }
2:remote management的listening address及listening port
为了启用remote managed port,我们需要在服务器的启动脚本中加入如下内容,如果你觉得不做验证、non-ssl很不爽,你可以使用SSL及password file做认证,具体可以参考:http://www.j2ee.me/j2se/1.5.0/docs/guide/management/agent.html
set JAVA_OPTIONS=-Dcom.sun.management.jmxremote.port=9192 %JAVA_OPTIONS%
set JAVA_OPTIONS=-Dcom.sun.management.jmxremote.authenticate=false %JAVA_OPTIONS%
set JAVA_OPTIONS=-Dcom.sun.management.jmxremote.ssl=false %JAVA_OPTIONS%
1
public
MBeanServerConnection getMBeanServerConnection(String hostname, String listeningPort)
2 {
3 MBeanServerConnection conn = null ;
4 try {
5 url = " service:jmx:rmi:///jndi/rmi:// " + host + " : " + port + " /jmxrmi " ;
6 JMXServiceURL serviceURL = new JMXServiceURL(url);
7 connector = JMXConnectorFactory.connect(serviceURL);
8 conn = connector.getMBeanServerConnection();
9 } catch (Exception e){
10 e.printStackTrace();
11 }
12 return conn;
13 }
2 {
3 MBeanServerConnection conn = null ;
4 try {
5 url = " service:jmx:rmi:///jndi/rmi:// " + host + " : " + port + " /jmxrmi " ;
6 JMXServiceURL serviceURL = new JMXServiceURL(url);
7 connector = JMXConnectorFactory.connect(serviceURL);
8 conn = connector.getMBeanServerConnection();
9 } catch (Exception e){
10 e.printStackTrace();
11 }
12 return conn;
13 }
好了,我们拿到MBeanServerConnection后,说明我们已经attach到target JVM了,有了这个连接,我们可以为所欲为了,夸张了点哈。下面以classloading mbean为例,看看我们如何操作这样的MBean,如果从这些MBean信息,
1
private
void
checkClassLoadingInfo(MBeanServerConnection conn)
2 {
3 try
4 {
5 clMBean = (ClassLoadingMXBean)ManagementFactory.
6 newPlatformMXBeanProxy(conn, ManagementFactory.CLASS_LOADING_MXBEAN_NAME, ClassLoadingMXBean. class );
7 } catch (IOException ioe){}
8 System.out.println(clMBean.getTotalLoadedClassCount());
9 System.out.println(clMBean.getLoadedClassCount());
10 System.out.println(clMBean.getUnloadedClassCount());
11 // here we can turn on verbose of class loading dynamically
12 clMBean.setVerbose( true );
13 }
2 {
3 try
4 {
5 clMBean = (ClassLoadingMXBean)ManagementFactory.
6 newPlatformMXBeanProxy(conn, ManagementFactory.CLASS_LOADING_MXBEAN_NAME, ClassLoadingMXBean. class );
7 } catch (IOException ioe){}
8 System.out.println(clMBean.getTotalLoadedClassCount());
9 System.out.println(clMBean.getLoadedClassCount());
10 System.out.println(clMBean.getUnloadedClassCount());
11 // here we can turn on verbose of class loading dynamically
12 clMBean.setVerbose( true );
13 }
打开verbose class后,我们就可以看到class loading\unloading相关的信息了,如下:
[Loaded weblogic.servlet.FileServlet from file:/D:/beasys/wls1030/wlserver_10.3/server/lib/weblogic.jar]
[Loaded weblogic.utils.string.CachingDateFormat$ParseCacheHolder from file:/D:/beasys/wls1030/modules/com.bea.core.utils_1.4.0.0.jar]
[Loaded java.lang.StringIndexOutOfBoundsException from d:\beasys\wls1030\JDK160~1\jre\lib\rt.jar]
有了这些信息,我们就可以根据他们诊断类冲突之类的问题。如果需要关闭verbose class,直接在上面的程序中调用setVerbose(false)就可以了。
正如前面所说的那样,这样的Mbean有很多,具体的需要你参考JDK的management API。